Skip to content

DslRenderer Component

The <DslRenderer> component takes an ElucimDocument and renders it as a fully interactive Elucim scene or presentation — no JSX authoring required.

PropTypeDefaultDescription
dslElucimDocumentrequiredThe DSL document to render
classNamestringCSS class for the root wrapper
styleReact.CSSPropertiesInline styles for the root wrapper
themeElucimThemeUnified content theme. Inject CSS custom properties for theming. Values can be hex, named colors, or var() references. Same type used by ElucimEditor.
colorScheme'light' | 'dark' | 'auto'Semantic token resolution: injects light or dark CSS variables
poster'first' | 'last' | numberRender a static frame instead of an interactive player
controlsbooleanOverride the document’s controls setting (Player root only)
autoPlaybooleanOverride the document’s autoPlay setting (Player root only)
loopbooleanOverride the document’s loop setting (Player root only)
fitToContainerbooleanfalseWhen true, scene fills parent width and scales proportionally
onPlayStateChange(playing: boolean) => voidCalled when playback starts or stops
onRenderError(error: Error) => voidCalled when a React render error occurs inside the scene tree
fallbackReact.ReactNodeCustom UI shown when a render error occurs
onError(errors: Array<{path, message}>) => voidCallback fired when DSL validation fails
refReact.Ref<DslRendererRef>Imperative handle for programmatic control
import { DslRenderer } from '@elucim/dsl';
import type { ElucimDocument } from '@elucim/dsl';
const doc: ElucimDocument = {
version: '1.0',
root: {
type: 'player',
width: 800,
height: 600,
fps: 30,
durationInFrames: 90,
background: '#0a0a1e',
children: [
{
type: 'fadeIn',
duration: 30,
children: [
{ type: 'circle', cx: 400, cy: 300, r: 60, stroke: '#4fc3f7', strokeWidth: 3 },
],
},
],
},
};
export default function MyAnimation() {
return <DslRenderer dsl={doc} style={{ maxWidth: 800 }} />;
}

Attach a ref to DslRenderer for programmatic playback control:

import { useRef } from 'react';
import { DslRenderer } from '@elucim/dsl';
import type { DslRendererRef } from '@elucim/dsl';
function MyPlayer({ dsl }) {
const ref = useRef<DslRendererRef>(null);
return (
<>
<DslRenderer ref={ref} dsl={dsl} />
<button onClick={() => ref.current?.play()}>Play</button>
<button onClick={() => ref.current?.pause()}>Pause</button>
<button onClick={() => ref.current?.seekToFrame(0)}>Reset</button>
</>
);
}
MethodReturnDescription
getSvgElement()SVGSVGElement | nullAccess the underlying SVG element
seekToFrame(frame)voidJump to a specific frame
getTotalFrames()numberGet the total frame count
play()voidStart playback
pause()voidStop playback
isPlaying()booleanWhether currently playing

The poster prop renders a single static frame without playback controls — useful for thumbnails, social previews, or print.

<DslRenderer dsl={doc} poster="first" /> {/* Frame 0 */}
<DslRenderer dsl={doc} poster="last" /> {/* Final frame */}
<DslRenderer dsl={doc} poster={45} /> {/* Frame 45 */}

Presets provide shorthand dimensions so you don’t have to remember exact pixel values:

PresetWidthHeightUse case
'card'640360Social cards, thumbnails
'slide'1280720Slide decks, presentations
'square'600600Social media, avatars
{
"version": "1.0",
"root": {
"type": "player",
"preset": "card",
"durationInFrames": 60,
"children": [...]
}
}
import { DslRenderer } from '@elucim/dsl';
import doc from './my-animation.json';
export default function Page() {
return <DslRenderer dsl={doc} />;
}

DslRenderer validates the document before rendering. If the document is invalid, it displays an error message instead of crashing — making it safe to use with user-supplied or AI-generated JSON.

Errors are grouped by node with a collapsible raw JSON view for debugging.

// Invalid document — missing required "version" field
<DslRenderer dsl={{ root: { type: 'player' } } as any} />
// → Renders an error message describing what's wrong

Use the onError callback to intercept validation failures:

<DslRenderer
dsl={doc}
onError={(errors) => {
errors.forEach(e => console.warn(`${e.path}: ${e.message}`));
}}
/>

Use onRenderError and fallback to catch React render crashes inside the scene tree:

<DslRenderer
dsl={doc}
onRenderError={(error) => console.error('Scene crashed:', error)}
fallback={<div>Something went wrong rendering this animation.</div>}
/>

Use fitToContainer to make the scene fill its parent container width and scale proportionally:

<div style={{ width: '100%', maxWidth: 960 }}>
<DslRenderer dsl={doc} fitToContainer />
</div>

The SVG viewBox handles resolution-independent scaling — no CSS transforms needed.

The colorScheme prop controls which default token values are injected (light or dark palette):

<DslRenderer dsl={doc} colorScheme="dark" /> {/* Dark palette defaults */}
<DslRenderer dsl={doc} colorScheme="light" /> {/* Light palette defaults */}
<DslRenderer dsl={doc} colorScheme="auto" /> {/* Detect from background luminance */}

When set to "light" or "dark", the value is authoritative — it directly selects the palette without luminance detection. This is the recommended approach when theme values include CSS var() references, since var() strings can’t be parsed for luminance at render time.

When set to "auto" (or omitted), the renderer detects the background color’s luminance and selects the appropriate palette. This only works with resolved color values (hex, rgb), not var() references.

The theme prop injects CSS custom properties onto the renderer wrapper. Each key becomes --elucim-{key}:

<DslRenderer
dsl={doc}
theme={{
foreground: '#ffeedd',
background: '#2d1a0e',
accent: '#ff6b35',
}}
/>

Custom keys are supported — any key/value pair is converted to a CSS custom property. This is useful for embedding Elucim scenes in themed host pages.

Any color field in a DSL document can reference theme tokens using $token syntax. This lets a single JSON document adapt to any host theme:

{
"type": "circle",
"cx": 200, "cy": 200, "r": 80,
"stroke": "$accent",
"fill": "$surface"
}

At render time, "$accent" resolves to var(--elucim-accent, #4fc3f7) — using the host’s CSS value if set, or falling back to a default.

See Themes → Semantic Color Tokens for the full token reference.

Elucim respects the prefers-reduced-motion media query. When the user has requested reduced motion, the Player component skips animations and immediately shows the final frame.

This applies to both autoPlay scenes (which start at the final frame) and manual play triggers (which jump to the end instead of animating).

Use the useReducedMotion() hook in your own components to match this behavior:

import { useReducedMotion } from '@elucim/core';
function MyComponent() {
const reducedMotion = useReducedMotion();
// true when prefers-reduced-motion: reduce is active
// Updates reactively if the user changes their preference
}

Error and validation UIs use semantic CSS custom properties (--elucim-error, --elucim-warning, --elucim-surface) with hardcoded fallbacks. This means error displays automatically adapt to your theme while remaining readable without one.

If the root node is a presentation, the renderer outputs a full slide deck with navigation, transitions, and optional presenter notes — all driven by the JSON:

import { DslRenderer } from '@elucim/dsl';
import presentation from './calculus-explained.json';
<DslRenderer dsl={presentation} className="my-deck" />