Timeseries Chart
A line (and optional area) chart on TanStack Charts, themed by the chart tokens: grid and tooltip follow light and dark, and the deploy marker is a dashed rule so a before and after read at a glance.
const start = Date.UTC(2026, 10, 4);
const data = Array.from({ length: 48 }, (_, i) => ({
time: start + i * 3_600_000, // one point an hour
value: 120 + Math.round(60 * Math.sin(i / 6)) + i * 2,
}));
<TimeseriesChart
ariaLabel="Requests, last 48 hours"
series={[{ id: "req", label: "Requests", data, area: true }]}
deploy={{ time: start + 30 * 3_600_000, label: "Deploy" }}
/>Props
color
Installation
npx shadcn@latest add https://sproutboat.com/ikada/r/timeseries-chart.jsonThe CLI writes components/ikada/timeseries-chart.tsx and merges the ikada theme into your CSS. Or take it from the package: import { TimeseriesChart } from "@sproutboat/ikada/charts";
Usage
import { TimeseriesChart } from "@/components/ikada/timeseries-chart";
const start = Date.UTC(2026, 10, 4);
const data = Array.from({ length: 48 }, (_, i) => ({
time: start + i * 3_600_000, // one point an hour
value: 120 + Math.round(60 * Math.sin(i / 6)) + i * 2,
}));
<TimeseriesChart
ariaLabel="Requests, last 48 hours"
series={[{ id: "req", label: "Requests", data, area: true }]}
deploy={{ time: start + 30 * 3_600_000, label: "Deploy" }}
/>Examples
const start = Date.UTC(2026, 10, 4);
const data = Array.from({ length: 48 }, (_, i) => ({
time: start + i * 3_600_000, // one point an hour
value: 120 + Math.round(60 * Math.sin(i / 6)) + i * 2,
}));
<TimeseriesChart
aria-label="Requests, last 48 hours"
series={[{ id: "req", label: "Requests", data }]}
deploy={{ time: start + 30 * 3_600_000, label: "Deploy" }}
height={220}
/>const start = Date.UTC(2026, 10, 4);
const data = Array.from({ length: 48 }, (_, i) => ({
time: start + i * 3_600_000, // one point an hour
value: 120 + Math.round(60 * Math.sin(i / 6)) + i * 2,
}));
<TimeseriesChart
aria-label="Requests and errors, last 48 hours"
series={[
{ id: "req", label: "Requests", data, area: true },
{
id: "err",
label: "Errors",
color: "errors",
dashed: true,
data: data.map((p) => ({ ...p, value: Math.round(p.value / 20) })),
},
]}
height={220}
/><TimeseriesChart aria-label="Requests, last hour" series={[]} height={160} />Props
| Prop | Type | Description |
|---|---|---|
| seriesrequired | readonly TimeseriesSeries[] | The lines to draw: a label, a colour and the points of each. |
| deploy | DeployMarker | readonly DeployMarker[] | A dashed vertical rule with a label, in the `chart-deploy` color. |
| height | number | Height in pixels. Default 210; the width fills the container. |
| yDomain | readonly [number, number] | [min, max] of the value axis. Defaults to 0 up to the largest value, rounded up. |
| xDomain | readonly [number, number] | Time range in ms [from, to]. Defaults to the span of the data. |
| formatTime | ((time: number) => string) | Formats x ticks and the tooltip heading. Defaults to clock times ("14:30") for epoch milliseconds spanning up to two days, dates ("Nov 5") beyond that, and plain numbers otherwise. |
| locale | string | Number and date locale; defaults to the `LocaleProvider` value, else `en`. |
| emptyText | string | Shown in place of the plot when no series has a point. Default `No data for this range`. |
| formatValue | ((value: number) => string) | Formats y ticks and tooltip values. Default `(value: number): string => formatNumber(value)`. |
| formatTick | ((value: number) => string) | Formats y axis ticks. Defaults to compact numbers (1.5K) with no unit, so labels stay narrow. |
| xTicks | number | Approximate number of x ticks. Defaults to 5. |
| yTicks | number | About how many value ticks to draw. Default 4. |
| className | string | Classes merged onto the root element, after ikada's own. |
| aria-label | string | — |
| ariaLabel | string | — |
Source: src/charts/timeseries-chart.tsx