Axes
Axis types: category (bands), number, time, log. Binding is by position: bottom/top — the X axis, left/right — the Y axis.
Time axis
time accepts a Date, a timestamp or an ISO string; ticks snap to calendar boundaries, and the label format depends on the step (hours → days → months → years).
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Metric over Time' },
series: [{ type: 'line', xField: 'date', yField: 'value', name: 'Value', marker: { enabled: false } }],
axes: [
{ type: 'time', position: 'bottom' },
{ type: 'number', position: 'left' },
],
legend: { enabled: false },
};
}export function getData() {
const start = Date.UTC(2025, 0, 1);
const day = 24 * 60 * 60 * 1000;
const values = [41, 43, 47, 45, 49, 53, 51, 56, 58, 55, 61, 64, 62, 67, 70, 68, 73, 71, 76, 79, 77, 82, 85, 83, 88, 86, 91, 94, 92, 97];
return values.map((value, index) => ({ date: new Date(start + index * 3 * day), value }));
}Logarithmic axis
log — for data growing by orders of magnitude; ticks at powers of base (10 by default).
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Exponential Growth' },
subtitle: { text: 'logarithmic Y axis' },
series: [{ type: 'line', xField: 'year', yField: 'users', name: 'Users' }],
axes: [
{ type: 'category', position: 'bottom' },
{ type: 'log', position: 'left' },
],
legend: { enabled: false },
};
}export function getData() {
return [
{ year: '2018', users: 120 },
{ year: '2019', users: 540 },
{ year: '2020', users: 2400 },
{ year: '2021', users: 9800 },
{ year: '2022', users: 41000 },
{ year: '2023', users: 165000 },
{ year: '2024', users: 720000 },
{ year: '2025', users: 2900000 },
];
}Hierarchical categories
grouped-category: data values are [group, item] arrays; a row of groups with separators appears below the item labels:
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Revenue by Product Category' },
subtitle: { text: '$B, grouped categories year → quarter' },
series: [
{ type: 'bar', xField: 'period', yField: 'iphone', name: 'iPhone' },
{ type: 'bar', xField: 'period', yField: 'mac', name: 'Mac' },
{ type: 'bar', xField: 'period', yField: 'services', name: 'Services' },
],
axes: [
{ type: 'grouped-category', position: 'bottom' },
{ type: 'number', position: 'left' },
],
};
}export function getData() {
return [
{ period: ['2018', 'Q1'], iphone: 140, mac: 16, services: 20 },
{ period: ['2018', 'Q2'], iphone: 124, mac: 20, services: 30 },
{ period: ['2018', 'Q3'], iphone: 112, mac: 20, services: 36 },
{ period: ['2018', 'Q4'], iphone: 118, mac: 24, services: 36 },
{ period: ['2019', 'Q1'], iphone: 124, mac: 18, services: 26 },
{ period: ['2019', 'Q2'], iphone: 108, mac: 20, services: 40 },
{ period: ['2019', 'Q3'], iphone: 96, mac: 22, services: 42 },
{ period: ['2019', 'Q4'], iphone: 104, mac: 22, services: 40 },
];
}CrossLines
Reference lines and ranges in axis coordinates — with labels:
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';
export function createOptions(): ChartOptions {
return {
data: getData(),
title: { text: 'Latency p95' },
series: [{ type: 'line', xField: 'month', yField: 'latency', name: 'p95, ms' }],
axes: [
{
type: 'category',
position: 'bottom',
crossLines: [{ type: 'range', range: ['Apr', 'May'], label: { text: 'incident' } }],
},
{
type: 'number',
position: 'left',
crossLines: [{ value: 200, stroke: '#e5484d', label: { text: 'SLO 200 ms', color: '#e5484d' } }],
},
],
legend: { enabled: false },
};
}export function getData() {
return [
{ month: 'Jan', latency: 182 },
{ month: 'Feb', latency: 174 },
{ month: 'Mar', latency: 196 },
{ month: 'Apr', latency: 230 },
{ month: 'May', latency: 218 },
{ month: 'Jun', latency: 187 },
{ month: 'Jul', latency: 171 },
{ month: 'Aug', latency: 165 },
];
}Axis options
| Block | Options |
|---|---|
| number/log | min, max, nice, base (log) |
| time | min, max (Date/timestamp) |
| category | paddingInner, paddingOuter |
Full option list
| Option | Type | Default | Description |
|---|---|---|---|
type | 'number' | 'category' | 'time' | 'log' | 'ordinal-time' | 'grouped-category' | based on series | axis type |
position | 'bottom' | 'left' | 'top' | 'right' | based on type | axis side |
title.enabled | boolean | true when text is set | axis title |
title.text | string | — | title text |
title.fontSize | Pixels | 12 | title font size |
title.color | ColorValue | foreground | title color |
line.enabled | boolean | true (heatmap — off) | axis line |
line.stroke | ColorValue | theme muted | line color |
line.width | Pixels | 1 | line width |
tick.enabled | boolean | true (heatmap — off) | ticks |
tick.size | Pixels | 6 | tick length |
tick.width | Pixels | 1 | tick width |
tick.stroke | ColorValue | theme muted | tick color |
label.enabled | boolean | true | tick labels |
label.fontSize | Pixels | 11 | label font size |
label.fontFamily | string | theme font | typeface |
label.color | ColorValue | theme muted | label color |
label.spacing | Pixels | 4 | gap between label and tick |
label.format | string | — | format string (',.2f', '.0%', '%d %b') |
label.formatter | ({ value, index }) => string | — | programmatic formatting |
label.avoidCollisions | boolean | true | skip overlapping labels |
gridLine.enabled | boolean | true (heatmap — off) | grid lines |
gridLine.stroke | ColorValue | theme grid | grid color |
gridLine.width | Pixels | 1 | width |
gridLine.lineDash | Pixels[] | — | grid dash pattern |
interval.values | unknown[] | auto | explicit tick values |
interval.minSpacing | Pixels | 8 | minimum label spacing |
crossLines[].type | 'line' | 'range' | 'line' | line or range |
crossLines[].value | value | — | line coordinate |
crossLines[].range | [from, to] | — | fill range |
crossLines[].stroke | ColorValue | theme muted | line color |
crossLines[].strokeWidth | Pixels | 1 | line width |
crossLines[].lineDash | Pixels[] | — | dash pattern |
crossLines[].fill | ColorValue | theme muted | range fill |
crossLines[].fillOpacity | Fraction | 0.12 | fill opacity |
crossLines[].label.text | string | — | label text |
crossLines[].label.color | ColorValue | theme muted | label color |
crossLines[].label.fontSize | Pixels | 11 | label font size |
min (number, log) | number | data domain | lower bound |
max (number, log) | number | data domain | upper bound |
nice (number) | boolean | true | round the domain to “nice” bounds |
base (log) | number | 10 | logarithm base |
paddingInner (category, grouped-category) | Fraction | 0.2 (ordinal-time 0.25) | inner band padding |
paddingOuter (category, ordinal-time, grouped-category) | Fraction | 0.1 | outer band padding |
Horizontal axis labels are automatically thinned out when crowded (label.avoidCollisions: false disables this).
Overlays
The “no data” and “loading” states are enabled by default: empty data shows overlays.noData.text, and loading: true shows overlays.loading.text.