Skip to content

Legend

Enabled by default, at the bottom. Clicking an item hides/shows the series (toggleSeries).

Modular build

When building with grafit-charts/core, the legend is a separate module: register(legendModule).

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Team velocity' },
    series: [
      { type: 'bar', xField: 'sprint', yField: 'done', name: 'Done', stacked: true },
      { type: 'bar', xField: 'sprint', yField: 'carry', name: 'Carried over', stacked: true },
    ],
    // legend on the right; clicking an item hides the series
    legend: { position: 'right' },
  };
}
ts
export function getData() {
  return [
    { sprint: 'S1', done: 21, carry: 4 },
    { sprint: 'S2', done: 25, carry: 6 },
    { sprint: 'S3', done: 19, carry: 3 },
    { sprint: 'S4', done: 28, carry: 5 },
    { sprint: 'S5', done: 31, carry: 2 },
  ];
}

Options

OptionTypeDefaultDescription
enabledbooleantrueshow the legend
positionLegendPlacement'bottom'docking side + alignment, or a floating anchor (below)
floatingbooleanfalseoverlay the whole chart area instead of reserving space
offset{ x?: Pixels; y?: Pixels }0floating only: inset from the anchored edges
toggleSeriesbooleantrueclick toggles visibility
item.marker.sizePixels10item marker size
item.label.fontSizePixels12label font size
item.label.fontFamilystringtheme fontfont family
item.label.colorColorValueforegroundlabel color
background.fillColorValuepanel fill behind the items
background.strokeColorValuepanel border color
background.strokeWidthPixels1panel border width
background.cornerRadiusPixels4panel corner radius
background.paddingPixels | Padding8 / 0inner padding; 8 when fill/stroke is set
dataLegendItemOptions[]custom items (below)

The item name is the series name (or yField if no name is set). showInLegend: false on a series removes its item.

Items that don't fit are paginated: arrows ‹ 1/3 › appear at the bottom of the legend (a horizontal legend fits up to two rows per page). For pie/donut, clicking an item hides the sector.

Floating placement

position is the docking side plus an optional alignment along it: top-right docks the legend to the top edge aligned right (top centers). top-*/bottom-* lay items out in horizontal rows, left-*/right-* — in a vertical column; the first token sets the orientation.

With floating: true the legend stops reserving space and overlays the chart (CSS position: absolute style). It is anchored to the whole chart area — captions included — so a left-aligned title and a top-right floating legend sit on the same level:

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

// A floating legend anchored to the top-right corner of the whole chart —
// on the same level as the left-aligned title.
export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'Site traffic', textAlign: 'left', spacing: 4 },
    subtitle: { text: 'visits per month, thousands', textAlign: 'left', spacing: 12 },
    series: [
      { type: 'line', xField: 'month', yField: 'organic', name: 'Organic' },
      { type: 'line', xField: 'month', yField: 'ads', name: 'Ads' },
    ],
    legend: {
      position: 'top-right',
      floating: true,
      background: { fill: 'rgba(255, 255, 255, 0.85)', stroke: '#cbd5e1', cornerRadius: 6, padding: 10 },
    },
  };
}
ts
export function getData() {
  return [
    { month: 'Jan', organic: 42, ads: 18 },
    { month: 'Feb', organic: 48, ads: 22 },
    { month: 'Mar', organic: 55, ads: 21 },
    { month: 'Apr', organic: 61, ads: 27 },
    { month: 'May', organic: 58, ads: 33 },
    { month: 'Jun', organic: 67, ads: 30 },
    { month: 'Jul', organic: 74, ads: 36 },
    { month: 'Aug', organic: 71, ads: 41 },
  ];
}

offset insets the box from the anchored edges (x from left/right, y from top/bottom); along a centered axis a positive value shifts right/down. background draws a panel behind the items — handy over the plot.

Custom items

legend.data fully replaces the auto-derived series items. Useful when colors carry meaning inside a single series — e.g. a Gantt-style range-bar painted by a per-datum fill callback:

ts
import { getData } from './data';
import type { ChartOptions, LegendItemOptions } from 'grafit-charts';

// Custom legend items describe the bar statuses of a Gantt-style range-bar —
// something the auto-derived per-series legend cannot show.
const STATUS_COLORS: Record<string, string> = {
  done: '#22c55e',
  running: '#3b82f6',
  failed: '#ef4444',
  queued: '#94a3b8',
};

export function createOptions(): ChartOptions {
  const data = getData();
  const countOf = (status: string) => String(data.filter((datum) => datum.status === status).length);
  const legendItem = (status: string, name: string): LegendItemOptions => ({
    name,
    marker: { color: STATUS_COLORS[status] },
    value: countOf(status),
  });
  return {
    data,
    title: { text: 'Pipeline run' },
    subtitle: { text: 'task timeline, hours' },
    series: [
      {
        type: 'range-bar',
        xField: 'task',
        yLowField: 'start',
        yHighField: 'end',
        direction: 'horizontal',
        cornerRadius: 3,
        fill: ({ datum }) => STATUS_COLORS[String(datum.status)] ?? '#94a3b8',
      },
    ],
    legend: {
      data: [
        legendItem('done', 'Done'),
        legendItem('running', 'Running'),
        { ...legendItem('failed', 'Failed'), marker: { color: STATUS_COLORS.failed, size: 12 }, label: { fontWeight: 'bold', color: '#ef4444' } },
        legendItem('queued', 'Queued'),
      ],
    },
  };
}
ts
export function getData() {
  return [
    { task: 'extract', start: 0, end: 3, status: 'done' },
    { task: 'validate', start: 3, end: 5, status: 'done' },
    { task: 'transform', start: 5, end: 11, status: 'running' },
    { task: 'notify', start: 8, end: 10, status: 'failed' },
    { task: 'load', start: 11, end: 14, status: 'queued' },
    { task: 'report', start: 14, end: 16, status: 'queued' },
  ];
}
OptionTypeDescription
namestringdisplay text (required)
seriesstringbinds the item to a series for toggling
marker.colorColorValuemarker color; a bound item inherits the series color
marker.sizePixelsper-item marker size
labelFontOptionsper-item label font/color
valuestringvalue to the right of the label

series is matched against the series id first, then its name. A bound item toggles the series on click and dims when it is hidden; an item without series (or with an unknown reference) is static — it renders, but clicking does nothing. For pie/donut, bind to an individual sector by its label (or an explicit id#index); binding to the pie series as a whole is not supported.