Skip to main content

Components and JavaScript API

This page covers the JavaScript/TypeScript surface of the React binding: the <DataFlowPlayer> component and its props, icon registration, and syntax highlighting. For the shape of the JSON specification (spec), see the API Reference (JSON spec).

Not using React?

The same options exist in the three other packages — <dataflow-player> (custom element), <dfa-player> (Angular) and mountPlayer (the core, no framework). The prop table below is the reference for all of them; what differs is only how you pass them. See Packages and bindings.

import { DataFlowPlayer } from '@dataflow-animator/react';
import '@dataflow-animator/core/styles.css';

<DataFlowPlayer>

The component that compiles a spec and displays it in a media player. Only the spec prop is required.

PropTypeDefaultRole
specDataFlowSpec— (required)The specification to animate.
heightnumber | string420Scene height (e.g. 420, '60vh').
controlsbooleantrueShows the playback bar and step navigation.
exportablebooleanfalseAdds a button that opens the JSON spec (copy / download).
videoExportboolean | VideoExportConfigfalseAdds a button that renders the animation to a WebM, MP4 or GIF file.
autoPlaybooleanfalseStarts playback automatically.
loopbooleanfalseLoops playback at the end.
speednumber1Playback speed (1 = normal).
themePlayerTheme'default'Visual palette (see below).
mode'light' | 'dark' | 'auto''auto'Which variant of theme to show.
density'compact' | 'comfortable' | 'spacious''comfortable'Visual density (element size / spacing).
widthnumber | stringcontainerStage width. Read at mount, before the first measurement.
initialTnumber0Instant the player opens at, in ms. Read once, at mount.
debugbooleanfalseInternal timeline inspection overlay.
highlightHighlighterPrismCustom syntax highlighting (see below).
fallbackReactNodeRendered on the server and until the player mounts, in place of the loading indicator.
classNamestringAdditional CSS class on the root container.
styleCSSPropertiesInline styles on the root container.
labelsPartial<PlayerLabels>EnglishLocalises the player chrome (see below).
Changing the spec remounts the player

The player reads its options once, when it mounts, so changing any of them — spec included — rebuilds it. The current instant and play state are carried across, so scrubbing or editing a spec live stays smooth.

Remounting is keyed on the spec's structure, not its referential identity: a literal object rebuilt on every parent render costs a serialisation, not a rebuild. Memoizing it is still worthwhile on a hot render path, but it is no longer the difference between smooth and stuttering.

Only the first mount honours initialT and autoPlay: every remount after that reopens at the current instant and play state instead, which is exactly what makes editing options mid-scrub invisible.

The first mount is also the only one that waits for a paint (two frames), so the placeholder — and its loading indicator — is really on screen before the spec is compiled and measured. A remount happens immediately: the previous player is still there, and trading it for two frames of empty box would be a blink.

// fine — an equal spec does not remount anything
<DataFlowPlayer spec={{ nodes: [...], timeline: [...] }} />

// cheaper still on a frequently re-rendering parent
const spec = useMemo(() => ({ nodes: [...], timeline: [...] }), []);
<DataFlowPlayer spec={spec} />

For SSR rendering / hydration placeholder and Next.js / Docusaurus integration, see Installation.

Themes

The look of the player is two independent choices:

  • theme — the palette (default, dots, blueprint, pcb, chalk, terminal, paper, neon);
  • mode — which variant of that palette to show (light, dark, auto).

Every theme ships both a light and a dark variant, so switching one axis never forces the other. mode="auto" (the default) follows the host site when an ancestor carries data-theme="light|dark" (the Docusaurus convention), and the OS preference otherwise — the resolution is pure CSS, so it stays SSR-safe and needs no JavaScript.

// A chalkboard that still follows the reader's light/dark preference.
<DataFlowPlayer spec={spec} theme="chalk" mode="auto" />

// A PCB pinned to its dark variant, whatever the host does.
<DataFlowPlayer spec={spec} theme="pcb" mode="dark" />
note

If you style the player yourself, note that the root element carries data-theme (the palette) and data-mode (the light/dark variant), so your own CSS can hook either axis independently.

Loading…

Toggle this site's own light/dark switch to see the other variant of whichever theme is selected — the players above are all on mode="auto".

pcb and electrical schematics

With direction: 'circuit', wires are tinted by their net (the driving signal), which deliberately takes precedence over the palette's arrow colour: that colour carries meaning. The pcb theme still themes the board, the components and the labels around them.

Each palette is a set of --rdfa-* CSS variables scoped under .rdfa-player. To tweak one, override the variables on your own container rather than forking the stylesheet:

.my-diagram .rdfa-player {
--rdfa-accent: light-dark(#b91c1c, #f87171);
}

Exporting the spec (exportable)

With exportable, a button appears in the controls bar; it opens the JSON specification (colored) in a window, with two actions:

  • Copy to clipboard;
  • Download as a .json file.
<DataFlowPlayer spec={spec} exportable />

Copying and downloading are browser-side operations (navigator.clipboard, download anchor): they have no effect during SSR, the window only opens after hydration. Has no effect if controls is false.

Exporting a video (videoExport)

With videoExport, a second button appears in the controls bar. It opens a small panel — format, resolution, frame rate — and writes the animation to a file.

<DataFlowPlayer spec={spec} videoExport />

Pass an object to narrow what the panel offers:

<DataFlowPlayer
spec={spec}
videoExport={{ formats: ['mp4'], resolutions: [1080], frameRates: [30] }}
/>
FieldTypeDefaultMeaning
formatsVideoExportFormat[]all threeWhich formats the panel offers, in order.
resolutionsnumber[][360, 540, 720, 1080]Output heights offered, in pixels — 720 reads 720p.
frameRatesnumber[][15, 20, 24, 30, 60]Frame rates offered.
bitratenumber4_000_000Video bitrate. Ignored by GIF.
filenamestring'dataflow'Base name of the file, extension excluded.

A list with a single entry stops being a question: the control is shown as plain text instead of a dropdown, so the value is still visible without being adjustable. { formats: ['mp4'], resolutions: [1080] } therefore pins the output and leaves only the frame rate to choose.

The panel estimates before it commits

The three settings interact, so the panel shows how long the export will take and updates it as they change. That estimate is what makes the controls meaningful: on the same 30-second animation, 720p WebM reads about 8 s while 1080p60 GIF reads about 1 min 40 s — a difference worth seeing before pressing the button rather than after.

The first estimate comes from a measured cost model, and the two formats behave nothing alike: video is almost flat in resolution (the work is rasterising the DOM once, plus a hardware encoder), while GIF is linear in pixels (quantisation and LZW run in JavaScript). After one export the player uses what this machine actually did instead of the model, so later estimates are sharper.

It does not interrupt playback

The export mounts a second player off screen and walks it through virtual time with clock.seek. The player on screen keeps playing, at its own speed, while the file is being written — and the export never waits for real time, so it finishes in a fraction of the animation's own duration (roughly a tenth to a third of it across the demos on this site). A progress bar and a Cancel button sit in the menu while it runs.

Choosing a format

  • WebM is the fastest and the smallest. It plays in Chrome, Firefox and Edge — but not in Safari, and not in PowerPoint.
  • MP4 plays everywhere: Safari, slide decks, social platforms, chat apps. Reach for it whenever the file is going to be handed to someone else.
  • GIF auto-plays where a video will not — a GitHub README, a Markdown file. It defaults to a smaller frame and a lower rate, as the format usually does. These diagrams are flat colour, so the 256-colour palette costs nothing visible.

Exporting needs the WebCodecs API for WebM and MP4 (Chromium, Firefox and recent Safari); GIF does not. The encoders are loaded on demand, so a player that never exports downloads none of that code. Has no effect if controls is false.

Localising the chrome (labels)

The control bar and the JSON dialog carry user-visible strings — the buttons' aria-labels and titles (tooltips) and the dialog's title — in English by default. The labels prop overrides them key by key; any key you leave out keeps its English default:

<DataFlowPlayer
spec={spec}
exportable
labels={{
play: 'Lecture',
pause: 'Pause',
prevStep: 'Étape précédente',
nextStep: 'Étape suivante',
jsonSpec: 'Spécification JSON',
}}
/>

The full key list is the exported PlayerLabels type: restart, play, pause, prevStep, nextStep, progressBar, fullscreen, exitFullscreen, jsonSpec, download, copy, copied, copyToClipboard, close, closeDialog, loading.

loading is the odd one out: it is not part of the chrome but of the placeholder the player shows before it mounts, and which reveals a loading indicator when the wait lasts long enough to be worth naming — see SSR and hydration. It lives here for the same reason as the others: so the default stays in the core rather than in each binding.

These strings live in attributes read by tooltips and assistive technology — none of them is drawn on the stage. This site passes labels on every player it renders, which is why the chrome follows the page language on the French pages.

Custom icons

The icon field of a node first resolves to a known tech, then to a registered badge. Two functions feed these registries:

type IconSource = string | (() => SVGElement);

registerNodeIcon(type: string, icon: IconSource): void; // main node pictogram
registerSubIcon(name: string, icon: IconSource): void; // `icon` badge (tech)

An icon is SVG markup, or a factory returning an SVGElement when it has to vary:

import { registerSubIcon } from '@dataflow-animator/react';

registerSubIcon(
'k8s',
'<svg viewBox="0 0 24 24" fill="#326CE5"><path d="…" /></svg>'
);
// then: { id: 'orchestrator', type: 'cloud', icon: 'k8s' }

// a factory, called on every resolution
registerSubIcon('build', () => buildAnimatedGlyph());

Markup is parsed once, on first use, and cloned afterwards. A registration always wins over the built-in icon of the same name — including the stateful switch and push_button pictograms.

Global registry — register only once

registerNodeIcon / registerSubIcon mutate a module-level registry, shared across all player instances and across requests in SSR environments. Call them only once at application startup (entry file, _app.tsx, layout.tsx...), never in a component body or a useEffect. Registering never touches the DOM, so it is safe at module scope in a bundle that also runs on the server.

The getNodeIcon(type) / getSubIcon(name) readers allow you to inspect the registry if needed; both return a fresh SVGElement.

Syntax highlighting

By default, code (set_content in code mode, packet bodies) is highlighted by Prism, via the exported highlightCode function:

highlightCode: (code: string, language: string) => string; // type Highlighter

You can entirely replace it using the highlight prop — useful to plug in Shiki, Highlight.js, or avoid bundling Prism:

const myHighlighter: Highlighter = (code, language) =>
myEngine.toHtml(code, language);

<DataFlowPlayer spec={spec} highlight={myHighlighter} />;

The function receives the raw code and the declared language, and returns HTML. escapeHtml is also exported as a fallback without highlighting.

Advanced API (engine)

For custom integrations (custom rendering, tests, tooling), the pure engine is exported. Its surface is low level and may evolve more freely than the component:

ExportRole
compile(spec)Compiles a spec into a Timeline (ordered clips).
evaluate(timeline, t)Pure visual state at time t (ms).
stepIndexAt / nextStop / prevStopStep-by-step navigation and breakpoints.
computeLayout(spec)Relative node positions (ratios), without DOM.

These functions touch no DOM and can run on the server. The player's clock is not a React hook: it lives in the core as createPlayerClock, exported for hosts that drive mountStage themselves.