chartcn

Brush & Zoom

Data zoom with animated transitions and adaptive downsampling.

The useBrush hook and BrushSlider component provide data zooming with scroll-to-zoom, drag handles, spring-animated transitions, and optional LTTB downsampling for large datasets.

Evaporation
Rainfall

Quick start

import { BrushSlider, Chart, LineMark, useBrush, XAxis, YAxis } from "@exhibit/charts";

function ZoomableChart({ data }) {
  const { visibleData, startIndex, endIndex, onBrushChange, containerRef } =
    useBrush({
      data,
      defaultZoom: { start: 20, end: 80 },
      pointBudget: 200,
      pointBudgetKey: "value",
    });

  return (
    <div ref={containerRef}>
      <Chart data={visibleData} height={300} xDataKey="date">
        <XAxis />
        <YAxis />
        <LineMark dataKey="value" />
      </Chart>
      <BrushSlider
        data={data}
        dataKey="value"
        startIndex={startIndex}
        endIndex={endIndex}
        onChange={onBrushChange}
      />
    </div>
  );
}

useBrush options

OptionTypeDefaultDescription
dataT[]requiredFull dataset
defaultZoom{ start: number; end: number }full rangeInitial visible range as percentages (0-100)
minVisibleItemsnumber20Minimum points visible when zoomed in
zoomFactornumber0.08Scroll-to-zoom sensitivity (0-1)
pointBudgetnumberMax visible points; applies LTTB downsampling when exceeded
pointBudgetKeystringauto-detectedNumeric key for the LTTB accessor

useBrush return value

FieldTypeDescription
visibleDataT[]Sliced (and optionally downsampled) data for the current window
startIndexnumberCurrent window start index (animated)
endIndexnumberCurrent window end index (animated)
onBrushChange(range) => voidHandler for BrushSlider
containerRefRefObjectAttach to the scroll container for wheel-to-zoom

Adaptive point budget (LTTB)

Large datasets can have thousands of points, but a 600px-wide chart can only display ~600 distinguishable pixels. Rendering more points than that wastes SVG complexity without visual benefit.

The pointBudget option caps the visible point count using LTTB (Largest-Triangle-Three-Buckets) — the industry-standard algorithm used by Grafana, Plotly, and other visualization tools. LTTB preserves peaks, valleys, and visual shape far better than uniform decimation.

useBrush({
  data: thousandPoints,
  pointBudget: 200,        // never render more than 200 SVG points
  pointBudgetKey: "price", // optimise for this numeric field
});

When zoomed out (many points visible), LTTB reduces to the budget. When zoomed in past the budget, all actual data points are shown — no downsampling occurs.

The lttbDownsample function is also exported for standalone use:

import { lttbDownsample } from "@exhibit/charts";

const reduced = lttbDownsample(data, 100, (d) => d.value);

BrushSlider props

PropTypeDefaultDescription
dataRecord<string, unknown>[]requiredFull dataset (always the complete array)
dataKeystringrequiredKey for the sparkline preview
startIndexnumberrequiredCurrent start index
endIndexnumberrequiredCurrent end index
onChange(range) => voidrequiredCalled on handle/region drag
colorstringvar(--color-chart-1)Sparkline colour
heightnumber48Slider height in pixels

On this page