Charts
Candlestick Chart
An open-high-low-close financial series with visible axes and every exact value in a native table.
When to use it: Use it for ordered market prices where both the range and movement within each period matter.
Example
import {
CandlestickChart,
CandlestickChartCandle,
CandlestickChartPlot,
ChartDescription,
ChartTable,
ChartTitle,
ChartTooltip,
} from "@comp0/react";
const prices = [
{ x: 1, open: 184, high: 192, low: 181, close: 190 },
{ x: 2, open: 190, high: 194, low: 186, close: 188 },
{ x: 3, open: 188, high: 197, low: 187, close: 195 },
{ x: 4, open: 195, high: 198, low: 189, close: 191 },
] as const;
const formatDay = (value: number | Date) => `Day ${value}`;
const formatPrice = (value: number) => `$${value}`;
export function Example() {
return (
<CandlestickChart
values={prices}
xLabel="Trading day"
yLabel="Share price"
formatX={formatDay}
formatY={formatPrice}
className="w-full max-w-2xl rounded-lg has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-4 has-[:focus-visible]:outline-teal-600 dark:has-[:focus-visible]:outline-teal-400"
>
<ChartTitle className="text-base font-semibold text-zinc-950 dark:text-zinc-50">
Four-day share price
</ChartTitle>
<CandlestickChartPlot
aria-label="Candlestick chart showing four days of share prices"
className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
>
{(candle) => (
<CandlestickChartCandle candle={candle} className="group outline-none">
<line
x1={candle.x}
x2={candle.x}
y1={candle.highY}
y2={candle.lowY}
data-direction={candle.direction}
className="stroke-rose-600 data-[direction=up]:stroke-teal-600 group-data-active:stroke-zinc-950 dark:stroke-rose-400 dark:data-[direction=up]:stroke-teal-400 dark:group-data-active:stroke-white"
strokeWidth="2"
vectorEffect="non-scaling-stroke"
/>
<rect
x={candle.x - candle.width / 2}
y={candle.bodyY}
width={candle.width}
height={candle.bodyHeight}
data-direction={candle.direction}
className="fill-rose-600 stroke-rose-600 data-[direction=up]:fill-white data-[direction=up]:stroke-teal-600 group-data-active:stroke-zinc-950 dark:fill-rose-400 dark:stroke-rose-400 dark:data-[direction=up]:fill-zinc-950 dark:data-[direction=up]:stroke-teal-400 dark:group-data-active:stroke-white"
strokeWidth="2"
vectorEffect="non-scaling-stroke"
/>
<rect
aria-hidden="true"
x={candle.x - candle.width / 2 - 1.5}
y={candle.highY - 1.5}
width={candle.width + 3}
height={candle.lowY - candle.highY + 3}
rx="1.5"
className="pointer-events-none fill-none stroke-transparent group-data-active:stroke-zinc-950 dark:group-data-active:stroke-white"
strokeWidth="2"
vectorEffect="non-scaling-stroke"
/>
</CandlestickChartCandle>
)}
</CandlestickChartPlot>
<ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
The price reached its highest close on day three before falling on day four.
</ChartDescription>
<ChartTable className="mt-4 w-full border-collapse text-left text-sm [&_td]:border-t [&_td]:border-zinc-200 [&_td]:py-2 [&_th]:border-zinc-200 [&_th]:py-2 dark:[&_td]:border-zinc-800 dark:[&_th]:border-zinc-800">
<caption className="sr-only">Daily open, high, low, and close share prices</caption>
<thead>
<tr>
<th scope="col">Trading day</th>
<th scope="col">Open</th>
<th scope="col">High</th>
<th scope="col">Low</th>
<th scope="col">Close</th>
</tr>
</thead>
<tbody>
{prices.map((price) => (
<tr key={price.x}>
<th scope="row">{formatDay(price.x)}</th>
<td>{formatPrice(price.open)}</td>
<td>{formatPrice(price.high)}</td>
<td>{formatPrice(price.low)}</td>
<td>{formatPrice(price.close)}</td>
</tr>
))}
</tbody>
</ChartTable>
<ChartTooltip className="pointer-events-none z-50 rounded-md bg-zinc-950 px-2 py-1 text-sm text-white shadow-lg dark:bg-zinc-50 dark:text-zinc-950" />
</CandlestickChart>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
CandlestickChart
Native figure sharing ordered OHLC values, labels, and formatting with every part. Owns a DOM element.
ChartTitle
Native figcaption that visibly names the figure. Owns a DOM element.
CandlestickChartPlot / CandlestickChartCandle
SVG wicks and bodies positioned against visible time and value axes. Owns a DOM element.
ChartDescription
Visible prose summarizing the important movement. Owns a DOM element.
ChartTable
Native table composed with an explicit caption, headers, and rows from the chart values. Owns a DOM element.
ChartTooltipOptional
Optional floating OHLC label shown on hover or focus. Owns a DOM element.
Step by step
- 1
Add the main part
Start CandlestickChart with strictly increasing x values and valid open, high, low, and close prices.
- 2
Add the supporting parts
Add ChartTitle and CandlestickChartPlot; wrap each rendered candle in CandlestickChartCandle so one candle is tabbable and horizontal arrow keys reveal the rest.
- 3
Make the behavior clear
Add ChartDescription and ChartTable for persistent context and exact OHLC values; optionally add ChartTooltip for pointer and keyboard details.
Exampletsx <CandlestickChart values={prices} xLabel="Trading day" yLabel="Share price"> <ChartTitle>Four-day share price</ChartTitle> <CandlestickChartPlot aria-label="Candlestick chart showing four days of share prices"> {(candle) => ( <CandlestickChartCandle candle={candle}> <rect x={candle.x - candle.width / 2} y={candle.bodyY} width={candle.width} height={candle.bodyHeight} /> </CandlestickChartCandle> )} </CandlestickChartPlot> <ChartDescription>The highest close occurred on day three.</ChartDescription> <ChartTable> <caption>Daily OHLC values</caption> <thead> <tr> <th scope="col">Day</th> <th scope="col">Open</th> <th scope="col">High</th> <th scope="col">Low</th> <th scope="col">Close</th> </tr> </thead> <tbody> {prices.map((price) => ( <tr key={price.x}> <th scope="row">Day {price.x}</th> <td>${price.open}</td> <td>${price.high}</td> <td>${price.low}</td> <td>${price.close}</td> </tr> ))} </tbody> </ChartTable> <ChartTooltip /> </CandlestickChart>;
Keyboard
- ⇥
- Enters the chart at its current candle and leaves with one more Tab.
- →
- Moves to the next candle without wrapping.
- ←
- Moves to the previous candle without wrapping.
- Home
- Moves to the first candle.
- End
- Moves to the last candle.
- Esc
- Dismisses an open ChartTooltip.
Forms and accessibility
Charts are descriptive content and do not create form values.
Accessibility checklist
- Keep both axis labels visible and format ticks with the same units used in ChartTable.
- Give CandlestickChartPlot a concise aria-label that identifies the instrument and period.
- Wrap custom marks in CandlestickChartCandle to expose one roving tab stop; each candle receives its formatted time and OHLC values as a name.
- ChartTooltip is an optional visual enhancement for hover and focus; never make it the only source of a value.
- Include ChartTable so every open, high, low, and close value remains directly readable.
- Distinguish rising and falling candles with shape or fill treatment as well as color.
API reference
import { CandlestickChart, CandlestickChartCandle, CandlestickChartPlot, ChartDescription, ChartTable, ChartTitle, ChartTooltip } from "@comp0/react";CandlestickChart
DOM elementNative figure sharing ordered OHLC values, labels, and formatting with every part.
| Prop | Type | Description |
|---|---|---|
values | readonly CandlestickChartValue[] | Strictly increasing x values with finite open, high, low, and close values. |
xLabel | string | Visible headings for the time and value axes. |
yLabel | string | Visible headings for the time and value axes. |
openLabel | string | Table headings for each financial value. |
highLabel | string | Table headings for each financial value. |
lowLabel | string | Table headings for each financial value. |
closeLabel | string | Table headings for each financial value. |
formatX | (value) => string | Formatters shared by axis ticks and table cells. |
formatY | (value) => string | Formatters shared by axis ticks and table cells. |
ChartTitle
DOM elementNative figcaption that visibly names the figure.
CandlestickChartPlot / CandlestickChartCandle
DOM elementSVG wicks and bodies positioned against visible time and value axes.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Concise text alternative naming the instrument and period. |
yMin | number | Optional finite vertical scale bounds. |
yMax | number | Optional finite vertical scale bounds. |
yTickCount | number | Visible vertical-axis tick count; defaults to five. |
children | (candle: CandlestickChartCandleState) => ReactNode | Custom candle renderer receiving its OHLC value, direction, and SVG geometry. |
candle | CandlestickChartCandleState | Candle state passed from the plot to CandlestickChartCandle. |
CandlestickChartCandle children | ReactNode | SVG shapes grouped into one named, keyboard-reachable candle. |
ChartDescription
DOM elementVisible prose summarizing the important movement.
ChartTable
DOM elementNative table composed with an explicit caption, headers, and rows from the chart values.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Native caption, thead, tbody, and optional tfoot markup. |
ChartTooltip
OptionalDOM elementOptional floating OHLC label shown on hover or focus.
| Prop | Type | Description |
|---|---|---|
placement | PopoverPlacement | Side of the active mark; defaults to "top". |
offset | number | Distance from the active mark; defaults to eight pixels. |
children | ReactNode | (details: ChartValueDetails) => ReactNode | Custom content receiving the active candle's formatted details. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
CandlestickChartPlot / CandlestickChartCandle
| Style hook | Meaning |
|---|---|
[data-direction] | The candle closed "up", "down", or "unchanged" from its opening value. |
[data-active] | The candle currently reached by pointer or keyboard. |
ChartTooltip
| Style hook | Meaning |
|---|---|
[data-open] | A value tooltip is visible. |