Active Foundation

Search documentation

Search all pages, headings and text, in the current package or topic or in all documentation.

Date axes

Calendar days and instants on an x axis, and how ticks and labels follow from them.

@workspace/active-charts 6.0.0Status unknowncontent differs from the recorded release 6.0.0baseline ce081dc3

The package's own guide

This page is the package's docs/recipes/DATE-AXES.md, included unchanged from the package as it is in Foundation. Links to the package's internal design documents keep their text.

A first chartClient

How to put dates on a Cartesian x-axis without hitting the silent invalid-date trap. Background and the full per-axis reference live in ../API.md ยง9.1.

The trap in one sentence#

Bar-class charts (BarChart, Histogram, WaterfallChart, BoxPlotChart, RangeBarChart, DumbbellChart, LollipopChart) default to a category (scaleBand) x-axis, which stringifies a Date xKey to a non-ISO string ("Fri May 01 2026 โ€ฆ"); a formatDate(v as Date, โ€ฆ) formatter then cannot read it and returns an empty string, so the tick label is blank. (On the bar-class wrappers such a formatter no longer compiles; the trap remains reachable through as/any.)

The fix is always one of two patterns: pre-format to a category string (best for bars) or declare an explicit time scale (best for lines/areas).

Monthly bars#

Bars need a band scale for their width, so use a category axis and feed a pre-formatted month label.

TSX
const chartData = report.monthlyDistribution.map((entry) => ({  ...entry,  label: formatDateOnly(`${entry.month}-01`, 'month', locale),}));
<BarChart  data={chartData}  xKey="label"  xAxis="category"  series={[    {yKey: 'processed', name: t('chart.processed')},    {yKey: 'failed', name: t('chart.failed')},  ]}  stacked  height={220}/>;

Why not keep the Date and pass a formatter? On a band scale the formatter receives the stringified date, not a Date โ€” see the trap above. Pre-formatting keeps a single, locale-aware label per bar.

Daily lines#

Line and area charts use an ordinal-time (scalePoint) scale by default. That axis is temporal, so it always declares whether its values are calendar dates or instants (../API.md ยง9.1, ADR-027). A daily price is a calendar date:

TSX
<LineChart  data={dailyPrices}  xKey="date"  xAxis={{    temporalSemantic: 'calendarDate',    formatter: (v) => formatDateOnly(v, 'day-month', locale),  }}  series={[{yKey: 'close', name: t('chart.close')}]}  height={240}/>

A custom formatter reads the value the way the axis declares it: formatDateOnly for a calendar date (the day never moves), formatDate for an instant (read in the chart zone). Without a formatter, every surface already does this for you.

Range comparison (range bars)#

RangeBarChart is a bar-class chart, so the same category rule applies: pre-format the category label, keep the numeric low/high keys for the bar extent.

TSX
const chartData = quarters.map((q) => ({  ...q,  label: formatDateOnly(`${q.period}-01`, 'quarter-year', locale),}));
<RangeBarChart  data={chartData}  xKey="label"  xAxis="category"  yLowKey="min"  yHighKey="max"  height={260}/>;

Quick reference#

GoalChartx-axisxKey holds
Monthly / quarterly barsBarChart, RangeBarChartxAxis="category"pre-formatted label string
Daily / monthly line/areaLineChart, AreaChartxAxis={{temporalSemantic: 'calendarDate'}}Date (or ISO string)
Moments in timeLineChart, AreaChartxAxis={{temporalSemantic: 'instant'}}Date (or ISO string)
Real calendar spacingLineChart, AreaChartxAxis={{type: 'time', temporalSemantic: โ€ฆ}}Date

See also#

  • ../API.md ยง9.1 โ€” Axis Types โ€” default x-axis per chart and the formatter runtime-value table.

  • ../API.md ยง10.1 โ€” format string shorthand โ€” locale-neutral numeric formatting for value axes.

This page as Markdown