Gauge dial (KPI dashboard)

type: gauge renders a semicircle with track + value arcs, optional red/amber/green range bands, and a target tick. Click any row in the KPI grid to drive the dial; bands auto-flip direction based on whether higher or lower is better.

A live, editable Svelte 5 data grid example. Open the interactive demo or read the documentation.

What this example shows

`type: 'gauge'` renders a semicircle dial with: - a grey track arc - a coloured fill arc representing the current `gaugeValue` - optional red/amber/green range bands via `gaugeRanges` - an optional target tick via `gaugeTarget`

The grid holds the data; the dial reads one row at a time. Click any row to display that KPI on the dial.

Source code (163-chart-gauge.svelte)

<!-- Documented in: docs/help/charts.md -->
<script lang="ts">
  /**
   * 163. Gauge dial (KPI dashboard)
   * --------------------------------
   * `type: 'gauge'` renders a semicircle dial with:
   *  - a grey track arc
   *  - a coloured fill arc representing the current `gaugeValue`
   *  - optional red/amber/green range bands via `gaugeRanges`
   *  - an optional target tick via `gaugeTarget`
   *
   * The grid holds the data; the dial reads one row at a time. Click any
   * row to display that KPI on the dial.
   */
  import {
    SvGrid,
    SvGridChart,
    tableFeatures,
    rowSortingFeature,
    columnFilteringFeature,
    type ColumnDef,
    type SvGridApi,
    type ChartSpec,
  } from '@svgrid/grid'

  const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })

  type Row = {
    id: number; metric: string;
    value: number; target: number; max: number; unit: string;
  }
  const rows: Row[] = [
    { id: 0, metric: 'Uptime',                  value: 99.82, target: 99.9,  max: 100,  unit: '%'  },
    { id: 1, metric: 'p95 latency',             value:   180, target: 150,   max: 400,  unit: 'ms' },
    { id: 2, metric: 'Error rate',              value:  0.45, target: 0.30,  max: 5,    unit: '%'  },
    { id: 3, metric: 'NPS',                     value:    62, target:   55,  max: 100,  unit: ''   },
    { id: 4, metric: 'Trial -> paid conversion',value:   8.4, target:  12.0, max: 25,   unit: '%'  },
  ]

  const columns: ColumnDef<typeof features, Row>[] = [
    { field: 'metric', header: 'Metric', width: 200 },
    { field: 'value',  header: 'Value',  width: 100, align: 'right' },
    { field: 'target', header: 'Target', width: 100, align: 'right' },
    { field: 'max',    header: 'Scale',  width: 90,  align: 'right' },
  ]

  let api = $state<SvGridApi<typeof features, Row> | null>(null)
  let selected = $state<Row>(rows[0]!)

  /** Pick range bands based on whether higher or lower is better. p95
   *  latency + error rate are "lower better"; the rest are "higher better". */
  function ranges(r: Row): ChartSpec['gaugeRanges'] {
    const higherBetter = !['p95 latency', 'Error rate'].includes(r.metric)
    if (higherBetter) {
      return [
        { from: 0,           to: r.target * 0.7,  color: '#ef4444' },
        { from: r.target * 0.7, to: r.target,     color: '#f59e0b' },
        { from: r.target,    to: r.max,           color: '#16a34a' },
      ]
    } else {
      return [
        { from: 0,             to: r.target,     color: '#16a34a' },
        { from: r.target,      to: r.target * 1.5, color: '#f59e0b' },
        { from: r.target * 1.5,to: r.max,        color: '#ef4444' },
      ]
    }
  }

  const spec = $derived.by<ChartSpec>(() => ({
    type: 'gauge',
    categories: [],
    series: [],
    gaugeValue:  selected.value,
    gaugeTarget: selected.target,
    gaugeMin:    0,
    gaugeMax:    selected.max,
    gaugeUnit:   selected.unit,
    gaugeRanges: ranges(selected),
    width:  340,
    height: 230,
  }))
</script>

<section class="flex flex-col flex-1 min-h-0 gap-3">
  <div class="shrink-0 rounded-lg border px-4 py-3" style="border-color: var(--sg-border); background: var(--sg-header-bg);">
    <p class="text-sm font-semibold" style="color: var(--sg-fg);">
      Gauge dial - one KPI at a time
    </p>
    <p class="mt-0.5 text-xs" style="color: var(--sg-muted);">
      Click any row to display that KPI on the dial. The red/amber/green bands flip direction
      based on whether higher or lower is better. The target tick shows the goal value; the
      filled arc is the current reading.
    </p>
  </div>

  <div class="flex flex-1 min-h-0 gap-3">
    <div class="flex-1 min-h-0">
      <SvGrid responsive={true}
        data={rows}
        columns={columns}
        features={features}
        sortable
        filterable
        selectionMode="row"
        enableRowSummaries={false}
        rowHeight={36}
        containerHeight="100%"
        fitColumns={true}
        onApiReady={(a) => { api = a }}
        onRowClick={(e: { row: Row }) => { if (e.row) selected = e.row }}
      />
    </div>
    <div class="shrink-0 rounded-lg border p-3" style="width: 380px; border-color: var(--sg-border); background: var(--sg-bg);">
      <div class="mb-2 text-xs font-semibold" style="color: var(--sg-muted);">{selected.metric}</div>
      <SvGridChart {spec} />
    </div>
  </div>
</section>

View this example on GitHub

More Charts examples

  • Chart a selection (context menu) - With integrated charting enabled, the right-click menu gains a Chart selected range item: select a block of cells, right-click, and the chart panel opens scoped to that range - the Excel chart-this gesture, built in. The item appears only when charting is on and is appended to the default context menu automatically.
  • Chart view of the grid - The `chart` prop turns the same <SvGrid> into a chart, driven by the grid’s filtered + sorted rows (search + sort flow through). A view of the grid like board and scheduler, but the renderer is free: the grid lazy-loads a built-in view wrapping the standalone SvChart via rowsToChartSpec. Flip Table <-> Chart (bar / line / area) over one source of truth.
  • Integrated charts (no deps) - Chart the grid data with no external charting library. SvGridChart renders a ChartSpec; rowsToChartSpec aggregates the grid current (filtered/sorted) rows into one. Bar, line, area, pie - plus 100% stacked, top-N + Other, an average reference line, and double-click-to-isolate a series. Filter the grid and the chart re-aggregates live.
  • Scatter / bubble chart - A scatter plot maps two numeric measures (x vs y); a bubble chart adds a third via dot radius. type: scatter with series points [{ x, y, r }]. Spend vs revenue, sized by deals, coloured by region, with an average-revenue reference line. Filter the grid and the cloud re-plots.
  • Time-series chart (date axis) - xType: time spaces points by ACTUAL time - irregular date gaps render proportionally - and shows real date ticks. A referenceLines target/SLA line spans the plot; toggle 100% stacked to read each day as a share of its total. Line, stacked area, or stacked bar.