Documentation

Use with React

One Chart component for static states, transitions and sequences.

Import Chart from visdelta/react. React is an optional peer dependency (React 18 or 19); other VisDelta entries do not import it. The component owns mounting, asynchronous replacement and cleanup. Import visdelta/style.css once in your application.

SalesChart.tsx
'use client';

import { useMemo, useState } from 'react';
import { bar } from 'visdelta/bar';
import { Chart } from 'visdelta/react';
import 'visdelta/style.css';

export function SalesChart({ rows }: {
  rows: { category: string; sales: number; profit: number }[];
}) {
  const [progress, setProgress] = useState(0);
  const states = useMemo(() => {
    const a = bar(rows).x('category').y('sales');
    const b = a.y('profit');
    return [a, b, b.sort('profit', 'descending')];
  }, [rows]);

  const pair = useMemo(() => states.slice(0, 2), [states]);

  return (
    <section>
      <Chart state={states[0]} height={280} aria-label="Sales" />
      <Chart state={pair} progress={progress} height={280} />
      <Chart state={states} progress={progress * 2} height={280} />
      <label>
        Progress
        <input type="range" min={0} max={1} step={0.01} value={progress}
          onChange={event => setProgress(event.currentTarget.valueAsNumber)} />
      </label>
    </section>
  );
}

See one chart, three ways for a side-by-side example of the chain API, JSON spec and React.

Component contracts

state accepts a builder, a plain ViewSpec, or a non-empty readonly array of those values:

  • One state or a one-element array displays a static chart at progress 0.
  • Two states use the existing transition() runtime. Progress ranges from 0 to 1.
  • Longer arrays use sequence(). Progress uses authored state positions: 0 is the first state, 1 the second, and 1.5 is halfway from the second to the third.
  • Empty arrays report an error through onError or an error boundary.

Changing state does not automatically start playback. Automatic planner stages do not change the authored progress units.

Chart accepts data (named datasets), width, height, chartStyle, onReady, onError, and ordinary host attributes such as className, style and aria-label. The host's children belong to VisDelta; do not supply children or dangerouslySetInnerHTML. Builders carry their chart modules; plain specs still require the appropriate registered module.

Keep states, state arrays, named-data maps and chart-style modules stable between unrelated renders. Avoid creating a new array directly in JSX when its contents have not changed. Use module constants or useMemo; changing their identity prepares a replacement. Dimension prop changes also prepare a new controller; container resizing uses the runtime's existing resize handling. For height="auto", give the component host a CSS height, for example style={{ height: 400 }}. A replacement retains the previous progress, clamped to its range, unless progress is supplied. The old chart stays visible until the new one is ready. Failed replacements keep the old chart and call onError; without onError, the error reaches React's nearest error boundary.

Playback and custom controls

Omit progress when the controller owns playback. onReady exposes the existing controller API (ChartController is the transition/sequence controller union), including play(), pause(), progress() and resize():

const controller = useRef<ChartController | null>(null);

<Chart state={pair} height={320}
  onReady={change => {
    controller.current = change;
    return () => { controller.current = null; };
  }}
  onError={error => setError(error.message)}
/>
<button onClick={() => controller.current?.play({ from: 0, to: 1, duration: 800 })}>
  Play
</button>

Import useRef from React and ChartController from visdelta/react. The optional cleanup returned by onReady runs before replacement or unmount. The component destroys its controller; callers should not destroy it themselves. Changing callbacks does not recreate the chart.

Choose one progress owner: a controlled progress prop for a slider or scroll position, or the controller for playback. Buttons, looping, viewport detection and reduced-motion policies remain application concerns. After a CSS theme change, call the controller's resize() to refresh its colours.

Server rendering

The entry is marked 'use client'. Server rendering emits an empty host; chart rendering begins after mounting in the browser. This is not server-side SVG rendering. In Next.js, construct builder instances inside a client component rather than passing class instances across the server/client boundary. delta() remains DOM-free and can run on the server.

Other frameworks

Use the existing imperative API: create a transition when its host is attached, drive progress() or play(), and call destroy() when the host is removed.

On this page