---
title: Chart
url: "https://native-sdk.dev/docs/components/chart"
docs_index: /llms.txt
lastUpdated: 2026-10-16
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

Use `chart` to display line, area, or bar series over evenly spaced samples. Colors follow the active theme. The chart reports a series summary to accessibility and automation tools. Data is copied into the view-build arena and downsampled to the per-series point limit using index buckets that preserve minima and maxima. Axis labels and hover details are optional. Charts do not handle presses.

## Markup

A `chart` contains one or more `series` elements. `values` binds to a model iterable of numbers (`f32` in a Zig core): a field, declaration, or function. Axis bounds accept literals or scalar bindings. This example displays two line series with category labels, numeric ticks, and gridlines.

```html
<chart height="180" grid-lines="3" x-labels="{months}" y-labels="true" hover-details="true" label="Request latency">
  <series kind="line" values="{p95_ms}" label="p95" />
  <series kind="line" values="{p50_ms}" color="text_muted" label="p50" />
</chart>
```

The series set is static in markup; values change through bindings. `NaN` samples are not drawn. To align a short history to the right of a fixed window, pad its leading samples with `NaN`.

```ts
/// The 60-sample window, NaN-padded at the front while samples accumulate.
export function cpuSpark(model: Model): readonly number[] {
  const out: number[] = [];
  for (let i = model.cpuHistory.length; i < 60; i++) out.push(NaN);
  for (const sample of model.cpuHistory) out.push(sample);
  return out;
}
```

```zig
/// The 60-sample window, NaN-padded at the front while samples accumulate.
pub fn cpuSpark(model: *const Model, arena: std.mem.Allocator) []const f32 {
    const out = arena.alloc(f32, 60) catch return &.{};
    @memset(out, std.math.nan(f32));
    @memcpy(out[60 - model.cpu_len ..], model.cpuHistory());
    return out;
}
```

Unlabeled charts get a generated series summary (`chart: line cpu 60 pts last 0.42`) as their accessible name, so automation can assert on the data without pixels; `label` replaces it.

## Bar

`kind="bar"` draws one column per sample. A derived y domain includes zero. Set `baseline="true"` to draw the baseline, and provide an x label for each sample. The renderer thins labels when they would overlap.

```html
<chart height="180" baseline="true" x-labels="{weekdays}" hover-details="true" label="Builds per day">
  <series kind="bar" values="{daily_builds}" label="builds" />
</chart>
```

## Area

`kind="area"` fills a line to the baseline. Set `y-min` and `y-max` to keep the scale fixed as values change. This example uses a 0–1 range without category labels; hover details identify samples by index.

```html
<chart height="160" y-min="0" y-max="1" grid-lines="3" hover-details="true" label="Memory">
  <series kind="area" values="{memorySpark}" label="memory" />
</chart>
```

## Axis labels

`x-labels` binds to an iterable of strings, with one label per sample in sample order. Labels render below the plot and are thinned to fit. Downsampling removes category labels because bucket indices no longer correspond to the original samples. `y-labels="true"` adds numeric ticks to the left using steps of 1, 2, or 5 times a power of ten. Gridlines use the same tick positions.

## Hover details

`hover-details="true"` makes the plot hoverable: the pointer snaps to the nearest sample, a hairline cursor marks it (line series get a dot on the hovered point), and a floating card shows the sample's category label with every series' name and value. The card prefers the right side of the cursor and flips left at the window edge. This is interaction-only chrome — static renders never show it — and presses still fall through to whatever is around the chart. Every live preview on this page has it on: hover any of the three.

## Attributes

| Attribute       | Description                                                                                                                                                                                                                                                                             |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `y-min`         | chart: explicit y-domain floor (a number or one \{binding}); omit to derive from the data. Bars always force 0 into a derived domain.                                                                                                                                                   |
| `y-max`         | chart: explicit y-domain ceiling (a number or one \{binding}); omit to derive from the data.                                                                                                                                                                                            |
| `grid-lines`    | chart: horizontal token-hairline gridlines at even divisions (whole number; 0/omitted = none — gridlines are opt-in).                                                                                                                                                                   |
| `baseline`      | chart: draw a hairline at the baseline (zero clamped into the domain); true/false or a \{binding}.                                                                                                                                                                                      |
| `x-labels`      | chart: one \{binding} naming a model iterable of strings — one category label per sample, oldest first (label i names sample i). Drawn muted below the plot, deterministically thinned to fit; dropped when a series downsamples (bucketed indices no longer name the labeled samples). |
| `y-labels`      | chart: numeric y tick labels (true/false or a \{binding}). Ticks ride a nice-step lattice (1/2/5 x 10^k, exact at their precision); with grid-lines set, gridlines share the lattice so grid and labels never disagree.                                                                 |
| `hover-details` | chart: pointer-hover point details (true/false or a \{binding}) — hovering snaps to the nearest sample and floats a card with its label and every series' value. Interaction-only chrome (static renders never show it); presses still fall through.                                    |
| `stroke-width`  | chart: line stroke width override (plain number; default 1.5).                                                                                                                                                                                                                          |
| `width`         | Definite width (plain number); 0/omitted keeps the intrinsic sparkline-sized default (160x48).                                                                                                                                                                                          |
| `height`        | Definite height (plain number); 0/omitted keeps the intrinsic sparkline-sized default (160x48).                                                                                                                                                                                         |
| `grow`          | Flex grow factor.                                                                                                                                                                                                                                                                       |
| `padding`       | Uniform padding (plain number).                                                                                                                                                                                                                                                         |
| `key`           | Sibling-scoped identity key.                                                                                                                                                                                                                                                            |
| `global-key`    | Parent-independent identity: ids survive reparenting between containers.                                                                                                                                                                                                                |
| `label`         | Accessible name; omitted charts get a generated series summary ("chart: line cpu 60 pts last 0.42") so automation reads the data without pixels.                                                                                                                                        |

### Series

`values` is required on every series.

| Attribute | Description                                                                                                                                                                                                   |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`    | series: line, area, or bar (literal; default line). Area is a line filled to the baseline; band envelopes stay with the Zig builder (ui.chart).                                                               |
| `values`  | series: one \{binding} naming a \[]const f32 iterable (a model field, pub decl, or fn - the same sources for each accepts). Required. Pad a short history's leading gap with NaN samples - they draw nothing. |
| `color`   | series: literal color token name (a canvas ColorTokens field, e.g. accent, info, success, text\_muted); default accent. Series retheme with the palette.                                                      |
| `label`   | series: name for the chart's semantics summary ("cpu", "stars"); the kind tag stands in when empty.                                                                                                           |

## Programmatic construction (Zig)

Use the Zig builder for `.band` series with paired lower and upper values, or for a series set constructed at view time. `ui.chart` takes `ChartOptions` and a slice of `canvas.ChartSeries`. Supported kinds are `.line`, `.bar`, and `.band`; `fill` on a line corresponds to markup’s `kind="area"`.

```zig
ui.chart(.{
    .width = 420,
    .height = 180,
    .grid_lines = 3,
    .baseline = true,
    .x_labels = &month_labels,
    .y_labels = true,
    .hover_details = true,
}, &.{
    .{ .kind = .line, .fill = true, .label = "cpu", .values = model.cpu_samples },
    .{ .kind = .bar, .color = .text_muted, .label = "jobs", .values = model.job_counts },
})
```

## Options

`ChartOptions` fields (the builder-side equivalent of the attributes table):

<table>
  <thead>
    <tr>
      <th>
        Option
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `width`
      </td>

      <td>
        Definite plot width (same contract as

        `ElementOptions.width`

        ); 0 keeps the intrinsic sparkline-sized default (160x48).
      </td>
    </tr>

    <tr>
      <td>
        `height`
      </td>

      <td>
        Definite plot height (same contract as

        `ElementOptions.height`

        ); 0 keeps the intrinsic sparkline-sized default.
      </td>
    </tr>

    <tr>
      <td>
        `grow`
      </td>

      <td>
        Flex grow factor; flexes the plot instead of a definite size.
      </td>
    </tr>

    <tr>
      <td>
        `padding`
      </td>

      <td>
        Uniform padding (plain number).
      </td>
    </tr>

    <tr>
      <td>
        `y_min`
      </td>

      <td>
        Explicit y-domain minimum; null derives it from the data (bar series force 0 into a derived domain — bars always have a zero baseline).
      </td>
    </tr>

    <tr>
      <td>
        `y_max`
      </td>

      <td>
        Explicit y-domain maximum; null derives it from the data.
      </td>
    </tr>

    <tr>
      <td>
        `grid_lines`
      </td>

      <td>
        Horizontal token-hairline gridlines (0 = none; gridlines are opt-in, never default). At even divisions of the plot — or on the y-label tick lattice when

        `y_labels`

        is on, so grid and labels share positions.
      </td>
    </tr>

    <tr>
      <td>
        `baseline`
      </td>

      <td>
        Hairline at the baseline (zero clamped into the domain).
      </td>
    </tr>

    <tr>
      <td>
        `x_labels`
      </td>

      <td>
        Category labels for the x axis, one per sample index (empty = no x axis). Muted, in a reserved gutter, thinned to fit; dropped when a series downsamples.
      </td>
    </tr>

    <tr>
      <td>
        `y_labels`
      </td>

      <td>
        Numeric y tick labels on the nice-step lattice, in a measured gutter left of the plot.
      </td>
    </tr>

    <tr>
      <td>
        `hover_details`
      </td>

      <td>
        Pointer-hover point details: snap cursor, per-point dots on line series, and a floating card with the sample's label and every series' value. Interaction-only; presses still fall through.
      </td>
    </tr>

    <tr>
      <td>
        `stroke_width`
      </td>

      <td>
        Line stroke width override (default 1.5).
      </td>
    </tr>

    <tr>
      <td>
        `key`

        /

        `global_key`
      </td>

      <td>
        Widget identity, as on any element.
      </td>
    </tr>

    <tr>
      <td>
        `semantics`
      </td>

      <td>
        Widget semantics; the role defaults to

        `chart`

        , an empty label gets a generated series summary, and the accessibility value reports the first series' latest point.
      </td>
    </tr>
  </tbody>
</table>

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)