Not every X is a timestamp. A load test runs over concurrency levels, a ride over distance; a breakdown runs over regions, a poll over options. Line, bar and band series plot — and stream — over numbers or names as well as time.
01 — PICK THE AXIS TYPE
axis.x.type is 'time' by default. 'numeric' maps plain numbers, 'category' maps names. It is read once, when the chart is created — remount the chart (a React key) to switch.<ChartContainer axis={{ x: { type: 'numeric' } }}>…</ChartContainer><ChartContainer axis={{ x: { type: 'category' } }}>…</ChartContainer>02 — KEY THE POINTS BY X
Points carry
x in place of time. On a numeric axis the spacing can be irregular — the line draws at each x and the crosshair snaps to the nearest point. A point whose x doesn't fit the axis is skipped with a dev warning.<LineSeries data={[{ x: 1, value: 120 }, { x: 16, value: 940 }, { x: 64, value: 1235 }]} />03 — CATEGORIES COME FROM THE DATA
Each category gets one band, in the order the series first name it. Bars fill their band;
axis.x.padding sets the gap between bands (inner) and at the ends (outer), as fractions of a step. Drag and wheel are off on a category axis — set axis.x.panZoom to change that.<ChartContainer axis={{ x: { type: 'category', padding: { inner: 0.3, outer: 0.2 } } }}> <BarSeries data={[{ x: 'Europe', value: 42 }, { x: 'Asia', value: 35 }]} /></ChartContainer>04 — FORMAT THE X
<XAxis format> gets the number or the category name and formats the tick labels, the crosshair label and the tooltip header. crosshairMove and pointClick report it as x.<XAxis format={(x) => `${Number(x).toFixed(1)} km`} />05 — STREAM A NUMERIC AXIS LIKE TIME
Keep the points in state and pass them to
data. The diff keys on x: a point with a new, larger x is appended with its entrance animation, and .slice(-n) rolls the window while auto-scroll keeps the newest point in view. x must increase from point to point, as time does. Imperatively, appendData and keepLast do the same.setRide((prev) => { const last = prev[prev.length - 1]; return [...prev, { x: last.x + 0.2, value: 284 }].slice(-80);}); <LineSeries data={ride} />06 — UPDATE ANY CATEGORY
Pass the new array: every bar that keeps its band eases to its new value, and the Y axis eases with it. A new name adds a band and the bands refit so every category stays in view — a category axis doesn't scroll. Imperatively, pass the whole array to
setSeriesData; updateData replaces only the layer's last point.setVotes((prev) => prev.some((v) => v.x === pick) ? prev.map((v) => (v.x === pick ? { ...v, value: v.value + 1 } : v)) : [...prev, { x: pick, value: 1 }], // a new band);