caliper uidocs

Price target fan

Free

A price history that fans out into a forecast range.

Installs components/ui/price-target-fan.tsxDepends on d3-shapeMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/price-target-fan.json
Price and analyst targetsNow 135
Bear
120 −11.1%
Median
152 +12.6%
Bull
188 +39.3%

Price and analyst targets
DatePrice or medianLowHigh
2026-01-01100
2026-02-01105
2026-03-01110
2026-04-01110
2026-05-01115
2026-06-01115
2026-07-01120
2026-08-01125
2026-09-01125
2026-10-01130
2026-11-01130
2026-12-01135
2027-02-01140128152
2027-05-01146124170
2027-08-01152120188

When to use #

Use it for

  • Analyst targets and projections with a low, mid and high.
  • Showing uncertainty growing with time.

Skip it when

  • There is no forecast; use a line chart.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/price-target-fan.json

Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { PriceTargetFan } from "@/components/ui/price-target-fan"

export function TargetFan() {
  const history = Array.from({ length: 12 }, (_, index) => ({ date: `2026-${String(index + 1).padStart(2, "0")}-01`, value: 100 + index * 3 + ((index * 7) % 5) }));
  const forecast = [
    { date: "2027-02-01", low: 128, mid: 140, high: 152 },
    { date: "2027-05-01", low: 124, mid: 146, high: 170 },
    { date: "2027-08-01", low: 120, mid: 152, high: 188 },
  ];
  return (
    <div className="w-full max-w-xl">
      <PriceTargetFan label="Price and analyst targets" history={history} forecast={forecast} />
    </div>
  );
}

API reference #

PriceTargetFan

component
PropTypeDefaultDescription
label *string—
history *readonly PricePoint[]—
forecast *readonly PriceForecast[]—Future dates, ascending. The fan opens from the last historical value.
formatValue((value: number) => string)numberText
loadingboolean—
errorstring—
classNamestring—

Accessibility #

It exposes group and status semantics, with aria-label, aria-busy and aria-hidden kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.