# Price Chart

> A price over time against a baseline like the previous close: green above it, red below, with volume and each point's open, high, low, and close.

- Name: `price-chart`
- Status: beta
- Categories: charts
- File: the install writes `price-chart.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `recharts`, `react-is`. The install adds them.
- Other registry items: `@arbokui/chart`. The install adds them.
- Gallery: https://arbokui.com/components/price-chart
- Registry item: https://arbokui.com/r/price-chart.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/price-chart
   ```

## Use it for

- Showing how a price moved over a day, a week, or a year, like a stock, a coin, or a fund.
- Pass data with a time and a close for each point, oldest first. Add open, high, low, and volume to show them in the tooltip.
- Pass baseline, like the previous close, for the dashed line. Above it the line is green, below it red. Left out, the first close is the baseline.
- Switch ranges, as from tabs, and the new range draws in from the left as the old one fades. A new first point counts as a new range.
- Pass timeZone, like "America/New\_York", to show times where the market is.

## Avoid it for

- Several series at once. Use LineChart.
- Values that are not prices or have no baseline worth comparing to. Use AreaChart.
- A tiny trend in a table cell. Use Sparkline.

## Props

- `data`: `{ time: number | string | Date; close: number; open?, high?, low?, volume?: number | null }[]`. One point per time, oldest first. Points are spaced evenly, so nights and weekends leave no gap. A date and time with no zone, like "2026-10-29T14:30", is read as UTC. A point with no time is left out.
- `baseline`: `number`. The dashed line the price is measured from. Default: the first close.
- `title`: `ReactNode`. A visible title. It names the chart. TypeScript requires title, aria-label, or aria-labelledby.
- `aria-label`: `string`. Names the chart when it has no visible title.
- `valueFormat`: `Intl.NumberFormatOptions`. Default `US dollars, with three significant digits when every price is under a cent`. Formats prices in the tooltip, and on the axis in short form. The axis adds digits when short labels would repeat.
- `timeFormat`: `Intl.DateTimeFormatOptions`. Formats the tooltip's time. Default: the time of day for points within a day, the date for daily points.
- `tickFormat`: `Intl.DateTimeFormatOptions`. Formats the time axis. Default: hours for a day, days up to two months, months up to two years, then years.
- `timeZone`: `string`. Default `"UTC"`. The time zone for every date. Fixed, so server and browser print the same text. One the browser does not know falls back to UTC.
- `locale`: `string`. Default `"en-US"`. The locale for numbers and dates.
- `volume`: `boolean`. Default `true when any point has volume`. Shows volume bars along the bottom.
- `labels`: `{ close?, open?, high?, low?, volume?, baseline?, change?: string }`. Default `baseline "Start"`. The words for each value. Pass baseline: "Previous close" for a day.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Height: 5, 7, or 9 times --control-h-md, so density scales it.
- `loading`: `boolean`. Default `false`. Shows a soft area shape at the same height. When data arrives, it fades out while the price draws in.
- `error`: `ReactNode`. Shows this message in place of the chart. true shows a default message.
- `onRetry`: `() => void`. Shows a retry button under the error.
- `emptyText`: `ReactNode`. Default `"No data yet"`. Shown when there are no points.
- `className`: `string`. Extra classes. Later classes win, so className='h-96' replaces the height.

## Accessibility

- Renders a \<figure> named by its title, or by aria-label. TypeScript requires one.
- A hidden line reads the range at once: the last close, the change from the baseline, and the high and low.
- Tab focuses the chart. The arrow keys step through the points, and the tooltip reads out each one: its time, close, open, high, low, volume, and the change from the baseline.
- Up and down are said as a signed percent in the tooltip, not by color alone. The baseline is named in its pill.
- A line glides to the point under the pointer, and its dot grows in. The price labels fade back and the pill steps aside while it is read, since the tooltip carries the numbers. Escape hides the tooltip, the line, and the dot, and brings the labels back.
- A range on its way out takes no focus and no pointer, so Tab always lands on the chart being shown. Right to left, time still runs left to right. A long baseline name is cut short.
- The line draws in from the left once the chart is on screen. A new range draws in the same way as the old one fades. At 0ms everything draws at once, and Recharts skips the motion for reduced motion.
- Keeps one height while it loads, draws, fails, or has no data, so the page does not move.

## Examples

### A day against the previous close

```tsx
<PriceChart
  title="Price today"
  data={points}
  baseline={279.85}
  timeZone="America/New_York"
  labels={{ baseline: "Previous close" }}
/>
```

### Ranges that follow tabs

```tsx
const [range, setRange] = useState("1D");

<Tabs value={range} onValueChange={(next) => setRange(String(next))}>
  <TabsList aria-label="Range">
    <TabsTab value="1D">1D</TabsTab>
    <TabsTab value="1M">1M</TabsTab>
    <TabsTab value="1Y">1Y</TabsTab>
  </TabsList>
  <TabsPanel value={range}>
    <PriceChart title="Price over the range" data={byRange[range]} baseline={range === "1D" ? previousClose : undefined} />
  </TabsPanel>
</Tabs>
```

### While it loads

```tsx
<PriceChart title="Price today" data={data ?? []} loading={isLoading} />
```
