Duck-UIDuck-UI

Charts

Line, bar, area, scatter, and pie charts powered by uPlot

Charts

Duck-UI provides three chart components built on uPlot — a fast, lightweight charting library.

Chart Component (SQL-Driven)

The easiest way to render a chart. Pass a SQL query and Duck-UI handles the rest:

import { Chart } from '@duck_ui/embed'

<Chart
  sql="SELECT month, SUM(revenue) AS rev FROM sales GROUP BY 1 ORDER BY 1"
  type="bar"
  height={300}
/>

How data transforms:

  • First column → x-axis
  • Remaining columns → y-series

If the first column contains strings (e.g., month names), they become categorical labels. If numeric (e.g., timestamps), they're used as x-values directly.

-- Categorical x-axis (strings)
SELECT region, SUM(revenue) AS rev FROM sales GROUP BY 1
-- → xLabels: ['North', 'South', ...], data: [[0, 1, ...], [rev1, rev2, ...]]

-- Numeric x-axis (dates/numbers)
SELECT EXTRACT(EPOCH FROM date) AS ts, SUM(revenue) FROM sales GROUP BY 1
-- → data: [[ts1, ts2, ...], [rev1, rev2, ...]]

UChart (Low-Level)

Direct access to the chart with full control over data format:

import { UChart } from '@duck_ui/embed'

<UChart
  data={[
    [1, 2, 3, 4, 5],        // x-values
    [10, 25, 15, 30, 20],   // series 1
    [5, 15, 10, 20, 25],    // series 2
  ]}
  type="line"
  height={300}
  labels={['Revenue', 'Profit']}
  colors={['#2563eb', '#16a34a']}
/>

Data format is [number[], ...number[][]] (uPlot's AlignedData):

  • First array: x-values (shared across all series)
  • Subsequent arrays: y-values for each series

Chart Types

Line

<UChart data={data} type="line" height={300} />

Default chart type. Points with connecting lines. Supports x-axis drag selection via onRangeSelect.

Bar

<UChart
  data={[[0, 1, 2], [100, 200, 150]]}
  type="bar"
  height={300}
  xLabels={['Jan', 'Feb', 'Mar']}
/>

Categorical bar chart. Best with xLabels for category names.

Area

<UChart data={data} type="area" height={300} />

Line chart with filled area below. Same behavior as line but with fill.

Scatter

<UChart data={data} type="scatter" height={300} />

Points only, no connecting lines. Supports xy-drag selection.

PieChart

Canvas-based pie and donut charts:

import { PieChart } from '@duck_ui/embed'

// Pie chart
<PieChart
  values={[40, 30, 20, 10]}
  labels={['Electronics', 'Clothing', 'Food', 'Books']}
  size={300}
/>

// Donut chart
<PieChart
  values={[40, 30, 20, 10]}
  labels={['Electronics', 'Clothing', 'Food', 'Books']}
  donut={0.6}
  size={300}
/>

Set donut to a value between 0 and 1 to control the inner radius ratio. 0 = full pie, 0.6 = donut.

Sparkline

Minimal inline trend chart — no axes, legend, or cursor:

import { Sparkline } from '@duck_ui/embed'

<Sparkline data={[10, 20, 15, 25, 30, 28, 35]} width={140} height={28} color="#2563eb" />

Use inside KPI cards or table cells for inline trend visualization.

Axes

Configure axis labels and formatting:

<UChart
  data={data}
  type="line"
  height={300}
  axes={{
    x: { label: 'Date', format: 'date' },
    y: { label: 'Revenue ($)', format: 'currency' },
  }}
/>

Built-in format presets:

FormatOutput Example
'number'1,234 or 1.5K / 2.3M
'currency'$1,234.00
'percent'45.2%
'date'Jan 1, 2024 (from Unix timestamp)

Custom formatter:

axes={{
  y: {
    label: 'Weight',
    format: (value) => `${value} kg`,
  },
}}

Tooltip

Tooltips are enabled by default. Customize them:

import { UChart, tooltipPlugin } from '@duck_ui/embed'

<UChart
  data={data}
  type="line"
  height={300}
  tooltip={{
    formatValue: (v) => `$${v.toLocaleString()}`,
    formatX: (v) => new Date(v * 1000).toLocaleDateString(),
  }}
/>

// Or disable tooltips entirely
<UChart data={data} type="line" tooltip={false} />

The tooltip automatically repositions to stay within the chart bounds.

Event Handlers

Point Click

<UChart
  data={data}
  type="bar"
  height={300}
  onPointClick={(seriesIdx, dataIdx, value) => {
    console.log(`Clicked series ${seriesIdx}, point ${dataIdx}, value ${value}`)
  }}
/>

Range Selection

<UChart
  data={data}
  type="line"
  height={300}
  onRangeSelect={(min, max) => {
    console.log(`Selected range: ${min} to ${max}`)
  }}
/>

Works with line and area charts (x-axis drag) and scatter charts (xy-drag).

Themes

Switch between light and dark themes:

import { UChart, darkTheme } from '@duck_ui/embed'

<UChart data={data} type="line" theme={darkTheme} height={300} />

Create a custom theme:

const myTheme = {
  background: '#0f172a',
  textColor: '#e2e8f0',
  gridColor: '#1e293b',
  axisColor: '#475569',
  palette: ['#3b82f6', '#f59e0b', '#10b981', '#ef4444'],
  fontFamily: 'Inter, sans-serif',
  fontSize: 12,
}

<UChart data={data} type="line" theme={myTheme} height={300} />

Color Palettes

Three built-in palettes (8 colors each):

import { defaultPalette, darkPalette, colorblindPalette } from '@duck_ui/embed'
PaletteBest For
defaultPaletteLight backgrounds
darkPaletteDark backgrounds
colorblindPaletteAccessibility (safe for color-vision deficiency)

Override colors per chart:

<UChart data={data} type="bar" colors={['#2563eb', '#16a34a', '#dc2626']} height={300} />

Presets

Presets configure uPlot behavior for each chart type:

PresetCursor DragPointsScale
linePresetx-axisYesDiscrete, evenly-spaced
barPresetNoneNoDiscrete, categorical
areaPresetx-axisYesDiscrete, evenly-spaced
scatterPresetxy-boxYes (paths.points)Default

You typically don't need to use presets directly — set type="line" etc. and the correct preset is applied automatically.

Format Utilities

Standalone formatting functions:

import { formatNumber, formatCurrency, formatPercent, formatDate } from '@duck_ui/embed'

formatNumber(1500)       // '1.5K'
formatNumber(2300000)    // '2.3M'
formatCurrency(1234.5)   // '$1,234.50'
formatPercent(0.452)     // '45.2%'
formatDate(1704067200)   // 'Jan 1, 2024'

Build axis tick formatters:

import { buildAxisFormatter } from '@duck_ui/embed'

const fmt = buildAxisFormatter('currency')
fmt(1234) // '$1,234.00'

On this page