Skip to main content

LineChart

Change over Timechart

Trend over a continuous (usually time) axis.

Line chart, 3 series over Jul 3, 2026–Sep 25, 2026; EMEA peaks at 5.7 in Sep 25, 2026
import { LineChart } from "@elabs-ai/components-charts";
Install
npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts

Skipping the @source line is the most common first failure: Tailwind does not scan node_modules, so every component renders unstyled. The one-time wiring

Prompt for your coding agent

Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.

Add the brand-ui LineChart chart (from @elabs-ai/components-charts) to this project.

1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`.
2. Look LineChart up before writing code: `npx -y @elabs-ai/components-cli docs LineChart` prints its real props, variants, what it pairs with and what to avoid.
3. If @elabs-ai/components-charts is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-charts`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`.
4. Use it here: [the screen or file, and what it should show]
5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop.
6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.

Use it for

  • How did it change over time? Several series over time.
  • one or more measures over continuous time, where the trend itself is the point

Avoid

  • many overlapping series (more than about 6) — use small multiples (ChartMultiples), a stream area chart or a composed chart
  • A LineChart over unordered categories — a line asserts continuity; use a BarChart.
  • More than ~5 series in one plot — past the chart-1…chart-5 palette the series stop being distinguishable; facet instead.
  • Distinguishing series by color alone — add markers or direct labels for non-color users.

Works with

Contains

Examples

32 live examples

Default

With Reference Line

A labelled threshold on the series’ own y-scale — a target, a budget, an SLA. Drawn in `--chart-foreground` with a dash (meaning, not gridline) and a haloed label; it stays outside the reveal clip like `Grid`.

Multi Series

Loading

Loading placeholders, fixed plot height

Categorical X Scale

Linear X Scale

Non Date X Auto Fallback

Unplottable X Fallback

Custom Tick Format

Duplicate Label Collapse

Drilldown

Click anywhere on the plot — or Tab in and press Enter — to drill in.

Density comparison

#394: the axis tick label reads the `text-meta` ROLE (was the raw `text‑xs` utility, which `data-density`/#340 cannot reach). Two columns pin `data-density` on a plain wrapping div — NOT `<ThemeProvider>`, which writes `data-density` to `document.documentElement` and would race two columns — mirroring `Foundations/Typography → Density scale`'s pattern. The measured font-size is read live from the browser so the columns cannot claim a scaling they do not render. Must match `Gantt`'s already-density-aware timescale tick (11.25px compact / 12px comfortable / 12.75px spacious, styling-and-tokens.md).

Weekend Hollow

`markerStyle` (RM-028): a hollow dot means weekend, a filled dot means weekday — the lieflat "hairline line, weekend/weekday marker" idiom. Every point still gets a marker; only the fill/stroke resolution differs.

Top Three Peaks

`labelPeaks` (RM-028): the top-k highest points get an enlarged marker and a `HaloText` value label — lieflat's "top-2/top-3 peaks, enlarged and labelled" rule. This series has exactly three well-separated spikes, so all three are labelled.

Adjacent peaks forced apart (acceptance)

Barcode Floor

`periodTicks="day"` (RM-028): a `HairlineFloor` tick for every calendar day — 90 of them — whether or not a label renders there, with the long tick anchored to the first day of the week (Monday), not an index stride from the series' start date (#253). The labelled ticks above are a SEPARATE layer — `ninetyDaySeries` starts 2024-01-01, so they land at an even 22-day stride (`Jan 1, Jan 23, Feb 14, Mar 7, Mar 30`), not month boundaries; `periodTicks` never changes that. Acceptance: 90 ticks, every 7th long (a weekly boundary), the long tick painted in its own higher- contrast ink, and no collision between the labelled ticks at 400px width.

With Reference Band

Nulls — gap

`nulls="gap"` (the default) breaks the line at a non-numeric sample — a visible hole, never a silent zero or a straight bridge over missing data.

Nulls — connect

`nulls="connect"` draws one continuous line straight across the missing sample.

Nulls — zero

`nulls="zero"` — the pre-RM-112 default — reads a missing sample as 0.

Curve comparison

`curve` (RM-112) — a named alias over `@visx/curve`. The default changed from `"natural"` to `"monotone"`: `curveNatural` can overshoot past a flat plateau's own value (visible on the middle two, equal-height points below), which reads as a false peak; `curveMonotoneX` is built to never do that. `"step-after"` is shown as a third, structurally distinct alias.

Outline

`outline` (RM-112) paints a `--chart-background` halo under the coloured stroke, so a line keeps reading as one continuous path through a crossing with another series or a busy grid — Datawrapper's line-outline idiom.

Symbols

`symbols` (RM-112) — Datawrapper's line-symbols vocabulary. Unset placement on a ≤12-point series defaults to hollow markers at the first/last point only; a dense series (>12 points) stays unmarked unless `placement` is set explicitly. Same resolution rule, one shared helper, on `Area` too.

Focus on hover

`focusOnHover` (RM-112) dims every OTHER series to `SELECTION_EXCLUDED_OPACITY` while the pointer (or the legend) is over one — a spotlight for a busy multi-series chart. A wide, invisible hit-stroke keeps the hover target reliable even at the default, thin 2.5px stroke. `legend` (issue 545) is the only realistically focusable candidate for a keyboard user to reach the spotlight at all — Tab reaches a legend item, and focusing it drives the identical dim a mouse hover does (`ChartLegend`'s own `onFocus`/ `onBlur`); blurring restores full opacity.

Selection states

`selectionStates` paints the host’s tri-state on each category’s points: selected marks carry a compound foreground/background outline, excluded marks dim AND carry a full-opacity dashed frame, so the three states stay distinguishable in greyscale.

Legend toggle, right of plot

Legend from column keys

Built by hand from the SAME normalized series `AutoChart`'s `LineLegendFromColumnKeys` story forwards (`spec.series: [{ key: "ebikes" }, { key: "cargo" }]`, no `name` — the legend engine always labels an item by its `dataKey`, never a display `name`). Its `.legend-container` is byte-identical to that story's — proof that `AutoChart` forwards into `LineChart`'s own `legend` prop rather than running a second implementation (Acceptance-4).

Legend at xs and sm density

Density tiers the container legend engine (RM-118) reads from `ChartConfigProvider` (RM-072, a host-picked furniture tier — never derived from viewport width): `xs` has no room at all, so an explicitly-on legend renders nothing; `sm` keeps the legend but always stacks it (one item per line), the same as a `narrow` breakpoint.

Legend suppresses the redundant key row

R4 (sitting 3, RM-118): two long series names overflow RM-110's own end labels at a narrow width, which normally falls back to `SeriesKeyRow` — a compact swatch+name row inside the plot. But `legend` is also forced on here, and the container legend (unlike the pre-RM-118 inline `<ChartLegend>` child) STAYS visible and stacked at narrow by design (Acceptance bullet 1). Rendering both would show the same series list twice, so `legendVisible` (`time-series-chart-shell.tsx`) suppresses the key row whenever the container legend already covers that job — the plot itself never reserves less space, only the redundant row disappears.

Lived-in: growth desk (moving average, forecast, navigator, range)

The way a growth desk actually uses a line: two and a half years of weekly actives, a four-week moving average to read through the noise, an eight-week seasonal forecast at the end, a mini-chart navigator opened on the last year, and a range gesture on the time axis that sizes any period — the caption under the chart reads the intent's `datapoints`. Analytics, navigator and selection are three independent props on the same chart; each one alone leaves the DOM as it was (ADR 0040).

Palette

`palette` (RM-186): series with no `stroke` of their own take the palette's colours in order — here the two ends of the sequential ramp — instead of the single lead-line colour they share when `palette` is unset.

API

LineChart

PropTypeDescription
datarequiredRecord<string, unknown>[]Data array - each item should have a date field and numeric values
xDataKeystringKey in data for the x-axis (date). Default: "date"
xScaleChartXScaleTypeHow `xDataKey` values are interpreted (#352). Default: `"time"`. - `"time"` — Date (or Date-coercible) x values on a time scale. - `"band"` — categorical x values (`"Turn 1"`, `"Step A"`), evenly spaced in first-seen order. Axis ticks, the ticker and the tooltip title all show your own value, not a formatted date. - `"linear"` — numeric x values spaced by magnitude. Omitting it keeps today's behaviour, except that a dataset whose x values are ALL non-Date-coercible degrades to `"band"` (with a dev warning) instead of collapsing.
marginnumber | Partial<Margin>Chart margins: one number for every side, or per side. Default: 40 on every side.
animationDurationnumberAnimation duration in milliseconds. Default: 1100
animationEasingstringCSS easing for clip-reveal. Default: cubic-bezier(0.85, 0, 0.15, 1)
enterTransitionTransition
revealSignaturestring
revealOnChartRevealOnWhen the enter reveal is allowed to play (#175). `"mount"` (default) plays as soon as the chart renders — no change from today. `"inView"` holds the reveal at width 0 until this chart's own container scrolls to 30% visible.
replayOnClickbooleanClicking the chart body replays the enter reveal (#175). Default `false`.
aspectRatiostringAspect ratio as "width / height". Default: "2 / 1", and "1.25 / 1" at the narrow tier (`DEFAULT_CHART_PLOT_HEIGHT`). `"auto"` leaves the height to an enclosing frame, or else to the caller's CSS.
plotHeightResponsive<ChartPlotHeight>The plot's own height (ADR 0039): px, or `{ aspect }` (width ÷ height), optionally per breakpoint. Wins over `aspectRatio`, which stays an alias.
classNamestringAdditional class name for the container
statusChartStatusLoading vs ready — drives chart phase and loading chrome. Default: `"ready"`.
loadingLabelstringCentered shimmer label while loading.
yDomainTweenDurationnumberAnimate y-domain over this duration (ms) on status transitions. Default: 500.
yDomainTweenbooleanAnimate y-domain when status or target domain changes. Default: true
xDomain[Date, Date]Visible x-domain for brush zoom.
xDomainSlotCountnumberFull dataset length for x-scale padding when `xDomain` is set.
tweenYDomainOnXDomainChangebooleanTween y-domain when brush changes the visible x-range. Default: false
styleCSSPropertiesInline container styles (e.g. fixed height for brush strip).
onPhaseChange(phase: ChartPhase) => voidFires when the internal chart phase changes (e.g. OG capture readiness).
childrenrequiredReactNodeChild components (Line, Grid, ChartTooltip, etc.)
onDatapointClickChartDatapointClickHandlerDrill-down (#349). Fires when a datapoint is activated by pointer OR keyboard. Setting it mounts a keyboard-operable target layer OUTSIDE the aria-hidden SVG — one tab stop, arrow keys to traverse. Leaving it unset changes nothing: no extra DOM, no new focusables.
copyValueOnActivatebooleanPut the datapoint's exact value on the clipboard when it is activated — the recovery path for a compact axis label. Default `false`; a chart with no interaction props still renders byte-identical DOM. A consumer-supplied `onDatapointClick` always wins.
datapointLabelChartDatapointLabelOverride the accessible name of each keyboard drill-down target (#349).
maxInteractiveDatapointsnumberDev-warning threshold on the number of keyboard targets. Default 500 (#349).
accessibleLabelChartA11yProps["accessibleLabel"]Accessible name for the chart region (announces to AT on focus).
accessibleDescriptionChartA11yProps["accessibleDescription"]Supplemental description read by AT (e.g. series names + value range).
nullsNullsModeContainer-level default for a `Line`'s own `nulls` prop (RM-112). Unset — every `Line` keeps its own default (`"gap"`).
focusOnHoverbooleanHovering (or, on touch, tapping) one series dims every other series to the shared selection-excluded opacity (RM-112, `dw-river.md` §2.3). Default false — today's behaviour.
legendContainerLegendPropLegend engine (RM-118): `true` or a config object mounts `ChartLegend` beside the plot via `useContainerLegend`; `{ interactive: "toggle" }` hides a series and re-tweens the y-domain. Unset (default) renders NOTHING new (R1, moved into `useContainerLegend` itself) — RM-110's end labels stay the default multi-series key for `LineChart`. `{ values: true }` prints each series' last point inside the visible x window (navigator, zoom or `xDomain`), in the format of the `YAxis` on its `yAxisId`; plain numbers when the series' axes format differently.
annotationsreadonly ChartAnnotation[]Declarative annotations in data units: text notes, ranges, reference lines, row notes.
analyticsreadonly ChartAnalytic[]Statistical overlays computed from `data` (ADR 0040 §1): computed `line`/`band`s (average, median, percentile, std-dev, CI) drawn through the annotation layer, and `trend`/`window`/`forecast`/`errorBars` drawn as derived series with a legend entry, a tooltip row and an accessible sentence. Unset: no change.
tooltipbooleanShow a hover/focus tooltip. Default `true`: with no `<ChartTooltip>` child the chart adds a default one; a `<ChartTooltip>` child (for `variant`, `rows`, `content`, …) replaces it. `false` turns the default off.
paletteChartPaletteColour ramp for the series (RM-186): each series child that sets no colour of its own takes the next colour of `resolvePalette(palette, n)`. Unset: every such series keeps `--chart-line-primary`, as before.
selectionStatesChartSelectionStatesResolver<Record<string, unknown>>Maps a mark (category, optional series key, optional raw row) to its selection state. Unset → the chart renders exactly as before.
dimExcludedbooleanDim `excluded` marks and add their non-hue channel (the dashed frame). Default `true`; `false` keeps the `data-selection` attribute (so a host can style it) but paints nothing.
hoverCategoryChartHoverCategoryThe category hovered in a SIBLING chart (or by the host). `null`/unset → none.
onHoverCategory(category: ChartHoverCategory) => voidFires with the category under the pointer on move, and `null` on leave.
scrollbarChartScrollbarModeStrip style. Default `"none"` — a chart never grows a strip on its own (ADR 0040 decision b); a `window` / `defaultWindow` turns it on, as does `"miniChart"` / `"bar"` / `"auto"`.
windowNavigatorWindow | nullControlled window.
defaultWindowNavigatorWindowInitial window for the uncontrolled case.
onWindowChange(window: NavigatorWindow | null, meta: NavigatorChangeMeta) => void
minSpannumberSmallest window the user can make: a duration in ms (time) or a row count (index). Default: 5× the median step (time; the stock-chart library' rule) / 3 (index).
align"start" | "end"Where an automatic first window sits. `"end"` = the latest data (the associative BI suite `scrollStartPos: 1`). Default `"start"`.
maxVisibleItemsResponsive<number | "auto">Category families (RM-141): how many categories the plot shows at once (the associative BI suite "Number of bars"). Default `"auto"` — as many as keep a readable band (`maxReadableCategories`); with `scrollbar="auto"` the strip appears only once the categories overflow that count.
maxVisiblePointsnumberTime-series families: with `scrollbar="auto"`, rows above this show the strip. Default 2000.
windowDomain"all" | "visible"Category families: `"all"` (default) keeps the value axis on the FULL data's domain while scrolling; `"visible"` refits it to the window.
zoombooleanPinch-to-zoom along the x axis (time x): two fingers on a touch screen, a trackpad pinch, or Ctrl/⌘ + wheel narrow the same window the strip moves; a two-finger drag pans it. `+` / `−` / `0` on the focused chart and the zoom buttons that appear once zoomed do the same from the keyboard. No strip is needed; with one, it follows. Default `true`; `false` leaves the gestures to the page. Off while the caller drives `xDomain` itself.
selectionGesturesreadonly ChartSelectionGesture[]Gestures to enable. Unset → no gesture layer, DOM byte-identical to today.
onSelectionIntentChartSelectionIntentHandler<Record<string, unknown>>Fires with every intent. Required for the gesture layer to mount.
selectionConfirmChartSelectionConfirmDefault `"immediate"`.
selectionFieldstringThe field name carried in intents. Default: the chart's `xDataKey`.
selectionHitRuleChartSelectionHitRuleRect / lasso hit rule. Default `"overlap"` (the report-builder BI suite).
selectionToolbar"auto" | "none"`"auto"` (default): a toolbar appears when gestures are enabled; `"none"` hides it.

Also accepts: ChartSelectionProps, ChartHoverLinkProps, ChartNavigatorProps, ChartSelectionGestureProps, FrameSizeGroupProps, Pick<LegendGroupProps, "legend">, Pick<ChartStateGroupProps, "status">, Pick<TooltipGroupProps, "tooltip">

Line

PropTypeDescription
dataKeyrequiredstringKey in data to use for y values
yAxisIdstring | numberY-scale group id (Recharts `yAxisId`). Default: `"left"`.
strokestringStroke color. Default: var(--chart-line-primary)
strokeWidthnumberStroke width. Default: 2.5. Set to 1 for a lieflat-style "hairline" line.
curveCurveFactory | CurveAliasCurve interpolation — a named alias (`"linear"` | `"monotone"` | `"natural"` | `"step"` | `"step-before"` | `"step-after"`, RM-112) or a raw `@visx/curve` factory. Default: `"monotone"`. `"natural"` (`curveNatural`, this package's pre-RM-112 default) is offered for parity only — Datawrapper's own guidance is to avoid natural/cardinal interpolation because it overshoots between two equal neighbours (`.claude/rules/charts.md` "Honesty").
animatebooleanWhether to animate the line. Default: true
fadeEdgesFadeEdgesFade the line stroke toward transparent at the chart edges. - `true` fades both edges, `false` disables the fade entirely. - `"left"` / `"right"` fades only that side. Default: true
showHighlightbooleanWhether to show highlight segment on hover. Default: true
showMarkersbooleanRender scatter-style circle markers at each data point. Default: false
markersSeriesPointMarkerStyleMarker styling (same options as Scatter).
markerStyle(d: Record<string, unknown>, index: number) => MarkerVariantPer-point marker style — `"filled"` | `"hollow"` | `"none"`, decided per data point (RM-028). E.g. `(d) => (isWeekend(d.date) ? "hollow" : "filled")` — the lieflat "hollow dot = weekend, filled = weekday" idiom. Setting this renders a marker at every non-`"none"` point regardless of `showMarkers` — the per-point fill/stroke decision takes over from the shared marker style, while `markers.radius`/`ringGap`/`strokeWidth` still apply as the shared geometry. A hollow marker fills with `--chart-background` (the plot ground) so it stays theme-safe with no `dark:` override. Default: unset — no per-point styling, today's behaviour.
symbolsSeriesSymbolsSpecPlacement/style wrapper over `showMarkers`/`markers` (RM-112, Datawrapper's line symbols: 8 shapes, first-and-last/all placement, filled/hollow style). Setting this turns markers on regardless of `showMarkers`. `placement` — `"all"` | `"ends"` | `"first"` | `"last"`, default `"ends"`. `style` — `"filled"` | `"hollow"`, default `"hollow"`. `shape`/`size` map onto `markers.shape`/`markers.radius`. Default unset: no symbols — today's behaviour, driven by `showMarkers`/`markers` alone.
nullsNullsModeHow this line draws a non-numeric sample (RM-112). Overrides the container-level `LineChart nulls` default. Unset — read the container default, or `"gap"` with no container default (a visible break, never a silent zero — the pre-RM-112 default was the "zero" behaviour, kept as `nulls="zero"`).
outlineboolean | numberPaint a `--chart-background` halo stroke under the series stroke (Datawrapper's line outline, May 2024) so two crossing lines still read apart. `true` uses a 2px halo on each side; a number sets the halo width in px. Default `false` — no halo, today's behaviour.
labelPeaksLabelPeaksSpecLabel the top-k highest points on the line (RM-028) — lieflat's "top-2/top-3 peaks, enlarged and labelled" rule (L3 Barcode Lollipop). A bare number is the peak count at the default {@link DEFAULT_PEAK_MIN_GAP} -sample spacing; pass `{ count, minGap }` to override spacing. A peak within `minGap` samples of an already-picked (necessarily higher — peaks are picked highest-first) peak is skipped — "the barcode lesson: adjacent peaks must be forced apart" — so two close peaks label only the taller one. Uses the exported {@link spacedTopK} helper. Default: unset — no peak labels, today's behaviour.
namestringSeries display name — the text of its end label, key item and auto summary (RM-110). Default: `dataKey`.
seriesLabelResponsive<SeriesLabelMode>Where the series names itself (RM-110): `"end"` — a label at its last point (the chart reserves right margin for it); `"key"` — a swatch + name in a key row above the plot; `"none"`. Takes a `Responsive` value, e.g. `{ base: "end", narrow: "key" }`. Default: in a chart with two or more `Line`/`Area` series, a series whose `name` is set and differs from its `dataKey` gets `{ base: "end", narrow: "key" }` when a `ChartLegend` is composed, else `"end"`; every other series gets `"none"`. An explicit value always wins. Colliding end labels are nudged apart, or dropped and restated `sr-only`.
valueLabelsChartValueLabelsAutomatic value labels (RM-110): `{ placement: "first" | "last" | "all" | "peaks", count?, outline?, matchColor?, format? }`. Placed with every other label through one collision pass — a label that cannot be placed is dropped and restated `sr-only`. `labelPeaks={n}` is the alias `{ placement: "peaks", count: n }` (exact, uncompacted numbers).
dashFromIndexnumberData index from which the line stroke becomes dashed (inclusive). Useful for projecting incomplete periods, e.g. dashed from yesterday through today.
dashArraystringDash pattern for the tail segment when `dashFromIndex` is set. Default: "6,4"
dashStrokestringStroke color for the dashed tail (from `dashFromIndex` on) when it should read as a distinct series from the solid head — e.g. a projection tail in a quieter ink than the actual-data head. Default: `stroke` (today's behaviour — head and tail share one ink, differentiated by dash alone).
loadingbooleanShow the loading pulse overlay. Default: follows chart loading phase. Set `false` to disable even during loading.
loadingStrokestringStroke color for the loading pulse overlay. Default: var(--chart-foreground)
loadingStrokeOpacitynumberLoading pulse stroke opacity. Default: 0.5
loadingPulseModeLineLoadingPulseModeOverride pulse animation mode (loop / exit / enter).
onLoadingPulseCycleComplete() => voidCalled when a loop-mode pulse cycle completes.
seriesIndexnumberOverride the series index used for pattern/dash/marker differentiation under high decoration — needed when a single-series chart must differ from a sibling chart, e.g. sparklines. Does NOT affect layout.

LineChartLoading

PropTypeDescription
marginPartial<Margin>Chart margins
strokestringStroke color for the animated loading segment.
strokeOpacitynumberStroke opacity for the animated loading segment. Default: 0.5
gridStrokestringGrid line stroke (color and opacity via color-mix or oklch alpha).
gridShimmerStrokestringShimmer band stroke (color and opacity via color-mix or oklch alpha).
gridShimmerbooleanAnimate a shimmer band across grid lines. Default: true
gridShimmerLengthnumberShimmer band width in pixels. Default: 140
gridShimmerSpeednumberShimmer speed multiplier (higher = faster). Default: 1
gridShimmerSyncbooleanMatch shimmer loop to the loading line pulse (cycle + inter-loop pause).
labelstringCentered shimmer label text. Default: "Loading"
aspectRatiostringAspect ratio as "width / height". Default: "2 / 1"
plotHeightResponsive<ChartPlotHeight>The plot height, as on the chart it stands in for — px, `{ aspect }`, or per breakpoint. Wins over `aspectRatio`, so the placeholder holds the box the loaded chart will fill.
classNamestringAdditional class name for the container

LineLoadingPulseStroke

PropTypeDescription
pathDrequiredstring
modeLineLoadingPulseMode
loopEpochnumberBumps to restart loop cycles without remounting the stroke.
strokestring
strokeOpacitynumberStroke opacity for the animated segment. Default: 0.5
strokeWidthnumber
onCycleComplete() => void

XAxis

PropTypeDescription
numTicksnumber@deprecated Use `tickCount` — `numTicks` still wins when both are set, exactly as before (ADR 0042 A.2, row 6). Read until 7.0.0, with one development warning.
tickCountAxisTickCountTick target (RM-108). `"auto"` (default) derives it from the plot width — `tickTargetForWidth(innerWidth)`, about one tick per 90 px, clamped 2–10, and never more than the number of data rows on a time axis (6 monthly rows get at most 6 ticks, not 9 labels between them).
ticksDate[] | number[]RM-108 naming, shared with `YAxis`. On a time axis: an alias of `tickValues` (`tickValues` wins). On a numeric x with `domain`/`scale` (ScatterChart): exactly these raw x values.
domainAxisDomainNumeric x only (ScatterChart with numeric `xDataKey`, RM-108): pin either end of the x domain in raw units; `"auto"` keeps the data-derived end. Read by the chart container, so place `XAxis` as a direct child. Setting `domain` or `scale` also switches the labels from one per data row to a numeric ruler. Ignored on a time axis.
scaleValueScaleTypeNumeric x only (RM-108): `"linear"` (default), `"log"` (refuses data or a domain touching 0 — dev warning, renders linear) or `"sqrt"`.
positionXAxisOrientationWhich edge the labels sit on (RM-108). Default: `"bottom"`.
orientationXAxisOrientation@deprecated Use `position` — the same values (ADR 0042 A.2, row 10). Read until 7.0.0, with one development warning; when both are set, `position` wins.
titleReactNodeAxis title (RM-108).
titlePlacementAxisTitlePlacement`"outside"` (default) — in the margin beyond the tick labels, at the far end; `"inside"` — `HaloText` at the far end inside the plot.
tickerHalfWidthnumberWidth of the date ticker box for fade calculation. Default: 50
tickMode"domain" | "data"`"domain"` — evenly spaced ticks across the time domain (default). `"data"` — one label per data row at its x value (better with sparse or monthly bars).
tickFormat(value: Date) => stringOverride the default `Intl`-based tick label formatter. Applied to every tick this axis renders (generated or `tickValues`). #357. **Time scale only.** Ignored (with a dev warning) when the chart runs `xScale="band" | "linear"` — those modes project onto a synthetic instant, so a `Date` formatter would print a fabricated calendar date instead of your x value. Format categorical/numeric x values in your data. #352.
dateFormatDateFormatPreset | ((value: Date) => string)The date-format LADDER (RM-109): a preset from `date-format.ts` (`"year"` → `"yearShort"` → `"month"` → `"day"` → `"weekday"` → `"hour"` → `"minute"`), or a formatter function for full control. Unset (default) — the axis picks a rung itself via `dateFormatForSpan`, from the time domain's span and how many ticks are on screen, instead of the old fixed `"Mon d"` shape whatever the span. A ten-year series reads years; a 36-hour series reads hours. `tickFormat` still wins outright when both are set — it is the raw escape hatch this package has always had; `dateFormat` is the smart default underneath it. Same time-scale-only caveat as `tickFormat` (#352).
tickValuesDate[]Render exactly these tick positions, bypassing tick generation AND the default label-collision de-dupe entirely — use when the default de-dupe would collapse the axis (e.g. dense data whose formatted labels collide). #357. **Time scale only** — see `tickFormat`. #352.
periodTicksXAxisPeriodTicks | falseDraw a `HairlineFloor` (RM-017) below the plot — one hairline tick per calendar period, whether or not a data row falls on it, with the long tick anchored to a real calendar boundary (day → the first day of the week, week → the first week of the month, month → January), not an index stride from wherever the series happens to start (#253). Lieflat provenance: the ruled foot of a ledger page gives a reader the passage of time with no labels and no axis. This is a SEPARATE visual layer from the labelled ticks above (`numTicks` / `tickMode`) — it never changes which labels render or where. `false` (default) draws nothing — today's behaviour.

YAxis

PropTypeDescription
yAxisIdstring | numberScale group id (Recharts `yAxisId`). Default: `"left"`.
positionYAxisOrientationWhich side of the chart to render labels. Default: `"left"`.
orientationYAxisOrientation@deprecated Use `position` — the same values (ADR 0042 A.2, row 11). Read until 7.0.0, with one development warning; when both are set, `position` wins.
numTicksnumber@deprecated Use `tickCount` — `numTicks` still wins when both are set, exactly as before (ADR 0042 A.2, row 7). Clamped to {@link Y_AXIS_MIN_TICK_COUNT}–{@link Y_AXIS_MAX_TICK_COUNT}. Read until 7.0.0, with one development warning.
tickCountAxisTickCountTick target (RM-108). `"auto"` (default) derives it from the plot height — `tickTargetForHeight(innerHeight)`: 3 under 200 px, else 5.
ticksnumber[]Render exactly these tick values (RM-108), bypassing generation.
domainAxisDomainPin either end of the value domain (RM-108); `"auto"` keeps the data-derived end. Read by the chart container, so place `YAxis` as a direct child. On a length encoding (bars) the domain always includes 0 — a non-zero lower bound is ignored with a dev warning (`charts-honesty`).
scaleValueScaleTypeHow values map to pixels (RM-108). `"log"` refuses data or a domain that touches 0 (dev warning, renders linear); bars are linear only. Default: `"linear"`.
titleReactNodeAxis title — names the unit of every tick (RM-108).
titlePlacementAxisTitlePlacement`"outside"` (default) — above the top tick label in the margin; `"inside"` — `HaloText` at the top of the axis inside the plot.
labelPlacementAxisLabelPlacement`"outside"` (default) — tick labels in the margin beside the plot; `"inside"` — labels sit just above their grid line inside the plot.
valueFormatChartValueFormatHow tick values are rendered. Default: `"compact"` — `1.5M`, not `1500k` and not `1500000`. Pass `"number"` for every digit.
currencystringISO 4217 code for `valueFormat: "currency"`. Falls back to `ChartConfigProvider`, then `"USD"`.
formatValue(value: number) => stringCustom formatter for tick labels. Overrides `valueFormat` entirely when set.
unitstringUnit text appended to {@link unitOn} tick(s) — `<YAxis unit="%" />` paints `"80 %"` on that tick, not every tick (RM-109). The tooltip is the place a unit repeats on every value (the blog's "3.4 % unemployed", not a bare "3.4 %"); see `TooltipRow.unit`.
unitOn"last" | "first" | "all"Which tick(s) {@link unit} paints on. Default: `"last"`.
matchSeriesColorbooleanPaint the tick labels and title in the colour of this axis' series when it carries exactly ONE series; neutral otherwise (RM-121, Datawrapper's "label colour: match data"). The text goes through `seriesLabelInk` — the stroke mixed toward the label ink so it still reads at 4.5:1. Default `false`.
sideLabelReactNode | "auto"A caption naming which scale this is, above the tick column (RM-121). `"auto"` → "Left scale" / "Right scale" by `position`, through the locale seam (`charts.axis.leftScale` / `charts.axis.rightScale`).

ChartTooltip

PropTypeDescription
showDatePillbooleanWhether to show the date pill under the plot. Default: shown when it fits the chart's bottom margin (≥ 36px) — never painted over a small plot.
showCrosshairbooleanWhether to show the vertical crosshair line. Default: true
showDotsbooleanWhether to show dots on the lines. Default: true
indicatorColorstring | ((point: Record<string, unknown>) => string)Color for the crosshair/indicator line. When a function, receives the hovered point (e.g. for candlestick: match candle color from close vs open). Default: --chart-crosshair.
content(props: { point: Record<string, unknown>; index: number }) => React.ReactNodeCustom content renderer for the tooltip box
rows(point: Record<string, unknown>) => TooltipRow[]Custom row renderer - return array of TooltipRow
variantChartTooltipVariantBox layout preset (RM-119). `"rows"` (default) is today's stacked `ChartTooltipContent`. `"table"` renders `ChartTooltipTable` — one column per series with a header row, the date in the `<caption>` (Datawrapper's dual-axis / multi-series shape, `dw-charts.md` §2.22). `"inline"` renders no box at all — `ChartTooltipInline` paints the hovered (or `focus`-nearest) series' value directly at the mark with `HaloText` (the bikes chart). Ignored when `content` is set — a custom renderer already owns the whole box.
focusbooleanDrive RM-112's per-series dim (`SeriesHoverDim`) from the tooltip's OWN nearest-series resolution: whichever series' y position is closest to the pointer, at ANY x along the crosshair, counts as hovered — not only a direct hover on that series' own 2px stroke. Works standalone: it registers "focus requested" on the shared series-mode context, so the dim fires with no `focusOnHover` prop on the `LineChart`/`AreaChart` container (a container `focusOnHover` still works exactly as before, and the two compose — either one turns the dim on). Also the default target series for `variant="inline"`. Default false.
pinbooleanTap-to-pin (RM-119): on a coarse pointer, a tap keeps the tooltip open instead of clearing the instant the finger lifts. A second tap on the mark, `Esc`, or a tap outside the chart releases it (announced once via `role="status" aria-live="polite"`). Default: `true` only when the pointer is coarse (`(pointer: coarse)` media query); pass an explicit boolean to force either way regardless of pointer type.
valueInTitlebooleanSwap the tooltip box's own title for nothing — the hovered value moves to the chart's OWN title instead (RM-119, #610). Inside a `ChartFrame` that shows a title this is automatic: while a row is hovered, pinned or keyboard-focused the frame's title reads "Apr: Revenue 40" and is announced once through the frame's polite status; leaving restores the title. No wiring. Outside a frame (or in `chrome="bare"`, a custom `headerSlot`, the expanded dialog) only the box title is dropped — the date/category is still on `useChart().tooltipData` for a custom title. Default false.
dotColorstring | ((point: Record<string, unknown>, line: LineConfig) => string)Override tooltip dot fill. When omitted and `rows` is set, dot colors match row colors. When a function, receives the hovered point and line config.
childrenReact.ReactNodeAdditional content to show below rows (e.g., markers)
classNamestringCustom class name
springConfigSpringConfigPer-chart override for the crosshair / dot / date-pill spring.
boxSpringConfigSpringConfigPer-chart override for the floating-panel spring.
panelStyleReact.CSSPropertiesInline styles for the tooltip panel (background, blur, etc.).
unitstringUnit text appended to every DEFAULT row's value (RM-109) — the same `unit` `<YAxis>` paints on one tick. Set automatically from the chart's own `<YAxis unit>` when unset (the shell threads it through); pass it explicitly only to override. Ignored when `rows` is set — a custom row builder owns its own `TooltipRow.unit` per row.
valueFormatChartValueFormatSTYLE to borrow from the chart's own `<YAxis valueFormat>` for DEFAULT rows (RM-109) — a currency symbol, a percent sign — never abbreviation (`ChartTooltipContent`'s doc). Set automatically from `<YAxis valueFormat>` when unset; ignored when `rows` is set.
currencystringISO 4217 code for `valueFormat`'s `style: "currency"`.

Theming

The tokens each state resolves to. Change the token and every theme follows.

StateToken
statusstatus="loading" → LineChartLoading skeleton; "ready" → the plot