npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@immediate-diagram/react

v1.2.3

Published

React component library for [Immediate Diagram](https://github.com/immediate-diagram/immediate-diagram) — an LLM-native diagramming platform that transforms natural-language prompts into rich, stateful, animated SVG diagrams.

Readme

@immediate-diagram/react

React component library for Immediate Diagram — an LLM-native diagramming platform that transforms natural-language prompts into rich, stateful, animated SVG diagrams.

Render any of the 7 supported diagram types (flowchart, sequence, architecture, pie, donut, block, venn) from a concise text DSL — with theming, interactive zoom/pan, timeline playback, and accessibility built in.

Installation

npm install @immediate-diagram/react
# or
bun add @immediate-diagram/react
# or
yarn add @immediate-diagram/react
# or
pnpm add @immediate-diagram/react

Peer dependencies: React ≥ 18.0.0 and ReactDOM ≥ 18.0.0 must be installed in your project.

Quick Start

Render a diagram in 3 lines:

import { ImmediateDiagram } from "@immediate-diagram/react";

function App() {
  return <ImmediateDiagram dsl="flowchart\n  A --> B --> C" />;
}

That's it — parsing, layout, rendering, and theming are handled automatically.


Table of Contents


Components

ImmediateDiagram

The primary, all-in-one integration component. Takes raw DSL text and handles everything: parsing, layout, SVG rendering, theming, error display, and optional timeline playback.

This is the recommended starting point for most use cases.

import { ImmediateDiagram } from "@immediate-diagram/react";

<ImmediateDiagram
  dsl={`flowchart
  Start --> Process --> End`}
  theme="dark"
  interactive
  showControls
  onStateChange={(state) => console.log("State:", state)}
/>

Props — ImmediateDiagramProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | dsl | string | (required) | Raw IMD DSL source string to parse and render. | | theme | "light" \| "dark" | "light" | Theme for the diagram and playback controller. | | width | number \| string | "100%" | Container width (number = px, string = CSS value). | | height | number \| string | "auto" | Container height (number = px, string = CSS value). | | animate | boolean | true | Enable CSS transitions for state changes. | | showControls | boolean | undefined | Show playback controls. undefined = auto-detect (show if ≥ 2 states). | | autoPlay | boolean | false | Auto-play the timeline on mount. | | defaultTimeline | string | undefined | Name of the initial timeline to activate. | | playbackSpeed | number | 1 | Initial playback speed multiplier. | | interactive | boolean | false | Enable zoom/pan viewport (mouse wheel, drag, pinch). | | showZoomControls | boolean | false | Show floating zoom control overlay (requires interactive). | | initialZoom | "fit" \| "actual" | "fit" | Initial zoom: fit to container or 100% (requires interactive). | | minScale | number | 0.1 | Minimum zoom scale (requires interactive). | | maxScale | number | 10 | Maximum zoom scale (requires interactive). | | sanitize | boolean | false | Sanitize SVG through DOMPurify before rendering. | | className | string | — | Additional CSS class for the container. | | style | CSSProperties | — | Additional inline styles for the container. | | onStateChange | (state: string) => void | — | Called when the diagram transitions to a new state. | | onNodeClick | (nodeId: string) => void | — | Called when a diagram node is clicked. | | onTimelineEnd | () => void | — | Called when the timeline reaches the end. | | onError | (error, errorInfo) => void | — | Error callback for rendering failures. | | errorFallback | (error, reset) => ReactNode | — | Custom render error fallback. | | renderError | (error, source) => ReactNode | — | Custom parse error renderer. | | data-testid | string | "immediate-diagram" | Test ID for the container element. |

Imperative Ref — ImmediateDiagramRef

For programmatic playback control, use a ref:

import { useRef } from "react";
import { ImmediateDiagram, type ImmediateDiagramRef } from "@immediate-diagram/react";

function App() {
  const ref = useRef<ImmediateDiagramRef>(null);

  return (
    <>
      <ImmediateDiagram ref={ref} dsl={dsl} showControls />
      <button onClick={() => ref.current?.play()}>Play</button>
      <button onClick={() => ref.current?.goToState("peak")}>Go to "peak"</button>
    </>
  );
}

| Method | Description | |--------|-------------| | play() | Start or resume playback. | | pause() | Pause playback. | | stepForward() | Advance to the next state. | | stepBack() | Go back to the previous state. | | goToState(name) | Jump to a named state. | | setSpeed(speed) | Set playback speed multiplier. | | setTimeline(name) | Switch to a different timeline. | | getCurrentState() | Get the current state name. | | getTimelines() | Get all available timeline names. |

Playback Controller Behavior

The playback controller adapts to the number of states:

  • 0–1 states: No controller rendered.
  • 2 states: Compact toggle button.
  • 3+ states: Full controller with scrubber, speed, and loop controls.

DiagramViewer

Lower-level component that accepts a pre-parsed DiagramAST. Must be wrapped in a DiagramProvider.

Use this when you already have a parsed AST, need fine-grained control over layout/render options, or want to share a single DiagramProvider across multiple diagrams.

import { parse } from "@immediate-diagram/parser";
import { DiagramProvider, DiagramViewer } from "@immediate-diagram/react";

const ast = parse("flowchart\n  A --> B");

function App() {
  return (
    <DiagramProvider>
      <DiagramViewer ast={ast} />
    </DiagramProvider>
  );
}

Props — DiagramViewerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | ast | DiagramAST | (required) | Parsed diagram AST from the parser. | | stableViewBox | BoundingBox | — | Fixed viewBox for state transitions (prevents layout shift). | | debounceMs | number | — | Debounce delay for AST changes (ms). 100–300 recommended for live editing. | | lazy | boolean | false | Enable IntersectionObserver-based lazy rendering. | | lazyRootMargin | string | "0px" | Root margin for lazy IntersectionObserver. | | sanitize | boolean | false | Sanitize SVG through DOMPurify. | | tooltip | boolean | false | Enable interactive tooltips on diagram elements. | | tooltipSelector | string | ".imd-arc, .imd-circle, .imd-node-group" | CSS selector for tooltip-triggering elements. | | layoutOptions | object | — | Override layout engine defaults (maxWidth, padding, nodeSpacing, etc.). | | renderOptions | Omit<RenderOptions, "theme"> | — | Override SVG render options (theme is always set from context). | | className | string | — | Additional CSS class. | | style | CSSProperties | — | Additional inline styles. | | pageTheme | "light" \| "dark" | — | Page-level theme (auto-detected from document if omitted). | | borderRadius | number | 8 | Wrapper border radius (px). | | padding | number | 16 | Wrapper padding (px). | | skeletonWidth | number | 600 | Skeleton placeholder width (px). | | skeletonHeight | number | 400 | Skeleton placeholder height (px). | | skeletonVariant | "shimmer" \| "pulse" \| "none" | auto | Skeleton animation variant. | | onError | (error, errorInfo) => void | — | Error boundary callback. | | errorFallback | (error, reset) => ReactNode | — | Custom error fallback. | | aria-label | string | "Diagram" | Accessible label. | | data-testid | string | "diagram-viewer" | Test ID. |


DiagramViewerFromSource

Mid-level component that accepts a DSL string, parses it with error recovery, and renders. Must be wrapped in a DiagramProvider.

Useful when you want the parse-and-render pipeline but need to control the provider separately (e.g., shared font loading, custom theme).

import { DiagramProvider, DiagramViewerFromSource } from "@immediate-diagram/react";

function Editor({ source }: { source: string }) {
  return (
    <DiagramProvider theme="dark">
      <DiagramViewerFromSource source={source} debounceMs={200} />
    </DiagramProvider>
  );
}

Props — DiagramViewerFromSourceProps

Extends most DiagramViewerProps (except ast), plus:

| Prop | Type | Default | Description | |------|------|---------|-------------| | source | string | (required) | Raw IMD DSL source string. | | debounceMs | number | — | Debounce delay for source changes (ms). | | renderParseError | (errors, source, hasPartial) => ReactNode | — | Custom parse error renderer. |

When parse errors occur, the component shows both the partial diagram (if recoverable) and a RecoveryErrorPanel with all errors and suggestions.


InteractiveDiagramViewer

Full interactive viewer with zoom/pan. Extends DiagramViewer with:

  • Mouse wheel zoom (cursor-centered)
  • Click-drag pan
  • Double-click fit-to-view
  • Touch pinch-zoom on mobile
  • Keyboard shortcuts (arrow keys for pan, +/- for zoom, 0 to reset)
  • Block collapse/expand for block diagrams (click block labels)
  • Optional zoom controls overlay
  • Optional copy-to-clipboard button

Must be wrapped in a DiagramProvider.

import { DiagramProvider, InteractiveDiagramViewer } from "@immediate-diagram/react";

<DiagramProvider>
  <InteractiveDiagramViewer
    ast={ast}
    interactive
    initialZoom="fit"
    showZoomControls
    showCopyButton
    minScale={0.1}
    maxScale={5}
  />
</DiagramProvider>

Props — InteractiveDiagramViewerProps

Extends all DiagramViewerProps, plus:

| Prop | Type | Default | Description | |------|------|---------|-------------| | interactive | boolean | true | Enable zoom/pan interactivity. | | initialZoom | "fit" \| "actual" | "fit" | Initial zoom behavior. | | showZoomControls | boolean | false | Show floating zoom control overlay. | | showCopyButton | boolean | false | Show "Copy as image" button overlay. | | minScale | number | 0.1 | Minimum zoom scale. | | maxScale | number | 10 | Maximum zoom scale. | | onCopySuccess | () => void | — | Called after successful clipboard copy. | | onFallbackDownload | () => void | — | Called when clipboard write falls back to download. | | onCopyError | (message: string) => void | — | Called when copy/download fails. |

Block Collapse

For block diagrams, clicking a block label toggles collapse/expand. Collapsed blocks display as compact placeholder rectangles with a ▶ indicator and child count badge. This is implemented as a pure AST transformation before layout — the layout engine always receives a clean AST.


PlaybackController

Standalone playback transport bar for timeline-based diagrams. Wire it to the usePlaybackController hook.

import { PlaybackController, usePlaybackController, DiagramProvider } from "@immediate-diagram/react";

function TimelineViewer({ ast }) {
  const controller = usePlaybackController({
    timelines: ast.timelines,
    transitions: ast.transitions,
  });

  return (
    <DiagramProvider>
      <DiagramViewer ast={applyState(ast, controller.currentStateName)} />
      <PlaybackController controller={controller} theme="dark" />
    </DiagramProvider>
  );
}

Props — PlaybackControllerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | controller | PlaybackControllerState | (required) | State object from usePlaybackController(). | | theme | "light" \| "dark" | "light" | Theme for the controller UI. | | className | string | — | Additional CSS class. | | keyboardEnabled | boolean | true | Enable keyboard shortcuts (Space = play/pause, ←/→ = step). |


ZoomControls

Floating zoom control buttons for interactive viewers. Includes zoom in, zoom out, fit-to-view, and reset (1:1) buttons with a scale percentage display.

<ZoomControls
  onZoomIn={zoomIn}
  onZoomOut={zoomOut}
  onFitToView={fitToView}
  onResetZoom={resetZoom}
  currentScale={1.0}
  position="bottom-right"
  theme="light"
/>

Props — ZoomControlsProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | onZoomIn | () => void | (required) | Zoom in callback. | | onZoomOut | () => void | (required) | Zoom out callback. | | onFitToView | () => void | (required) | Fit-to-view callback. | | onResetZoom | () => void | (required) | Reset to 1:1 callback. | | currentScale | number | (required) | Current scale (1.0 = 100%). | | theme | "light" \| "dark" | "light" | Theme. | | position | "top-left" \| "top-right" \| "bottom-left" \| "bottom-right" | "bottom-right" | Corner position. | | className | string | — | Additional CSS class. |


CopyImageButton

Floating button to copy the rendered diagram as a PNG image to the clipboard. Falls back to file download when the Clipboard API is unavailable.

const containerRef = useRef<HTMLDivElement>(null);

<div ref={containerRef}>
  <DiagramViewer ast={ast} />
  <CopyImageButton containerRef={containerRef} position="top-right" />
</div>

Props — CopyImageButtonProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | containerRef | RefObject<HTMLElement> | (required) | Ref to the SVG container. | | theme | "light" \| "dark" | "light" | Theme. | | position | "top-left" \| "top-right" \| "bottom-left" \| "bottom-right" | "top-right" | Corner position. | | pixelRatio | number | 2 | Pixel ratio for rasterization (2 = Retina). | | backgroundColor | string | "transparent" | Background color of the rasterized image. | | padding | number | 16 | Padding around the diagram in the image (px). | | downloadFilename | string | "diagram" | Filename for fallback download (without extension). | | onSuccess | () => void | — | Called after successful copy. | | onFallbackDownload | () => void | — | Called on fallback to file download. | | onError | (message: string) => void | — | Called on error. | | className | string | — | Additional CSS class. |


DiagramTooltip

Interactive tooltip wrapper for diagram elements. Shows rich, cursor-tracking tooltips on hover/touch for arcs (pie/donut), circles (venn), and nodes (graph).

<DiagramTooltip theme="dark" enabled selector=".imd-arc, .imd-circle">
  <div dangerouslySetInnerHTML={{ __html: svgString }} />
</DiagramTooltip>

Props — DiagramTooltipProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | (required) | Content to wrap. | | theme | "light" \| "dark" | "light" | Tooltip theme. | | enabled | boolean | true | Whether tooltips are enabled. | | selector | string | ".imd-arc, .imd-circle, .imd-node-group" | CSS selector for tooltip-triggering elements. |

The tooltip reads data-label, data-value, data-percentage, data-node-id, data-set-id, and data-id attributes from the matched SVG elements.


ThemeToggle

Light/dark theme toggle button with animated sun/moon icons.

import { ThemeToggle, useThemePreference } from "@immediate-diagram/react";

function Header() {
  const { theme, toggleTheme } = useThemePreference();
  return <ThemeToggle theme={theme} onToggle={toggleTheme} />;
}

Props — ThemeToggleProps

| Prop | Type | Description | |------|------|-------------| | theme | "light" \| "dark" | Currently active theme. | | onToggle | () => void | Toggle callback. |

Note: Requires @immediate-diagram/react/styles.css for full visual styling.


Toast & ToastContainer

Notification system with auto-dismiss. Use the useToast hook for state management.

import { ToastContainer, useToast } from "@immediate-diagram/react";
import "@immediate-diagram/react/styles.css";

function App() {
  const { toasts, addToast } = useToast();

  return (
    <>
      <button onClick={() => addToast("Diagram copied!", "success")}>
        Copy
      </button>
      <ToastContainer toasts={toasts} onDismiss={(id) => {/* handled by hook */}} />
    </>
  );
}

ToastMessage

| Field | Type | Description | |-------|------|-------------| | id | string | Unique toast ID. | | message | string | Message text. | | variant | "success" \| "error" \| "info" | Visual variant (green/red/blue). |

ToastContainerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | toasts | ToastMessage[] | (required) | Active toasts. | | onDismiss | (id: string) => void | (required) | Dismiss callback. | | duration | number | 3000 | Auto-dismiss duration (ms). |


ConfirmDialog

Accessible confirmation modal dialog.

<ConfirmDialog
  open={isOpen}
  title="Delete Diagram"
  message="Are you sure? This action cannot be undone."
  confirmLabel="Delete"
  confirmVariant="danger"
  onConfirm={handleDelete}
  onCancel={() => setIsOpen(false)}
/>

Props — ConfirmDialogProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | open | boolean | (required) | Whether the dialog is visible. | | title | string | (required) | Dialog title. | | message | string | (required) | Dialog message. | | confirmLabel | string | "Confirm" | Confirm button label. | | confirmVariant | "danger" \| "primary" | "primary" | Confirm button variant. | | onConfirm | () => void | (required) | Confirm callback. | | onCancel | () => void | (required) | Cancel callback. |


DiagramWrapper

Theme-aware container that provides background styling, border, and visual boundary detection. Used internally by DiagramViewer but can be used standalone for custom compositions.

When the page theme and diagram theme differ, a subtle visual boundary (border) is automatically added to make the contrast feel intentional.

<DiagramWrapper metadata={ast.metadata} pageTheme="dark" padding={24}>
  {/* your diagram content */}
</DiagramWrapper>

Props — DiagramWrapperProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | metadata | DiagramMetadata \| null | — | AST metadata for theme resolution. | | children | ReactNode | (required) | Content. | | className | string | — | Additional CSS class. | | style | CSSProperties | — | Additional styles. | | pageTheme | "light" \| "dark" | auto | Page-level theme (auto-detected from DOM). | | borderRadius | number | 8 | Border radius (px). | | padding | number | 16 | Padding (px). | | aria-label | string | — | Accessible label. | | data-testid | string | — | Test ID. |


DiagramErrorBoundary

React error boundary for diagram rendering. Catches errors from layout, rendering, or SVG injection and displays a fallback UI with a retry button.

<DiagramErrorBoundary
  onError={(error, info) => logToSentry(error, info)}
  fallback={(error, reset) => (
    <div>
      <p>Something went wrong: {error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  )}
>
  <DiagramViewer ast={ast} />
</DiagramErrorBoundary>

Props — DiagramErrorBoundaryProps

| Prop | Type | Description | |------|------|-------------| | children | ReactNode | Components to wrap. | | onError | (error, errorInfo) => void | Error callback (for logging/telemetry). | | fallback | (error, reset) => ReactNode | Custom fallback UI. |


RecoveryErrorPanel

Displays multiple parse errors with line numbers, source context, and "Did you mean?" suggestions. Used internally by DiagramViewerFromSource and ImmediateDiagram.

<RecoveryErrorPanel
  errors={parseResult.errors}
  source={dslSource}
  hasPartialDiagram={parseResult.ast !== null}
/>

Props — RecoveryErrorPanelProps

| Prop | Type | Description | |------|------|-------------| | errors | RecoveryParseError[] | Parse errors to display. | | source | string | Original DSL source for context. | | hasPartialDiagram | boolean | Whether partial AST was recovered. | | data-testid | string | Test ID (default: "recovery-error-panel"). |


LoadingSpinner

Branded loading indicator with animated spinner ring and customizable message.

<LoadingSpinner message="Rendering diagram…" />

Props — LoadingSpinnerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | message | string | "Loading…" | Message below the spinner. | | data-testid | string | "loading-spinner" | Test ID. |

Note: Requires @immediate-diagram/react/styles.css for the animation.


Button

Generic styled button with variant support.

<Button variant="primary" onClick={handleSave}>Save</Button>
<Button variant="danger" onClick={handleDelete} disabled>Delete</Button>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | (required) | Button content. | | onClick | () => void | — | Click handler. | | disabled | boolean | false | Disabled state. | | variant | "primary" \| "secondary" \| "danger" | "primary" | Visual variant. |


Context & Provider

DiagramProvider

Provides the layout engine, text measurement (Canvas-based with font fallback), and theme configuration to all diagram components in the tree.

Required for: DiagramViewer, DiagramViewerFromSource, InteractiveDiagramViewer.

Not required for: ImmediateDiagram (creates its own provider internally).

import { DiagramProvider, DiagramViewer } from "@immediate-diagram/react";

function App() {
  return (
    <DiagramProvider theme="dark" fontFamily="Fira Code, monospace">
      <DiagramViewer ast={ast1} />
      <DiagramViewer ast={ast2} />
    </DiagramProvider>
  );
}

Props — DiagramProviderProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | fontFamily | string | "Inter, system-ui, Avenir, Helvetica, Arial, sans-serif" | CSS font-family for text measurement. | | layoutEngine | LayoutEngine | auto | Pre-configured layout engine (auto-created if omitted). | | theme | "light" \| "dark" \| Partial<DiagramTheme> | "light" | Theme preset or partial theme override. | | children | ReactNode | (required) | Child components. |

Theme Priority

Theme resolution follows this priority:

  1. DiagramProvider theme prop — highest priority (programmatic override)
  2. DSL theme dark directive — from the diagram source
  3. Default light theme — lowest priority

When DiagramProvider has an explicit theme prop, it overrides all DSL theme directives. When omitted, individual diagrams can control their own theme via the DSL.

useDiagramContext

Access the full diagram context value:

const { measureText, fontsReady, layoutEngine, theme, hasExplicitTheme } = useDiagramContext();

Returns a DiagramContextValue with:

| Field | Type | Description | |-------|------|-------------| | measureText | MeasureTextFn | Text measurement function (Canvas-backed or heuristic). | | fontsReady | boolean | Whether web fonts have loaded (measurement is accurate). | | layoutEngine | LayoutEngine | Shared layout engine with all strategies. | | theme | DiagramTheme | Current theme configuration. | | hasExplicitTheme | boolean | Whether the provider has a programmatic theme set. |

Convenience Hooks

These hooks are shortcuts for accessing specific context values:

| Hook | Returns | Description | |------|---------|-------------| | useEffectiveTheme(metadata) | DiagramTheme | Resolves theme considering provider + DSL theme directive. | | useDiagramTheme() | DiagramTheme | Returns the provider's theme directly. | | useFontsReady() | boolean | Whether web fonts are loaded. | | useLayoutEngine() | LayoutEngine | The shared layout engine. | | useMeasureText() | MeasureTextFn | The text measurement function. |

DiagramSkeleton

Loading placeholder shown while fonts are initializing:

import { DiagramSkeleton } from "@immediate-diagram/react";

<DiagramSkeleton width={600} height={400} variant="shimmer" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | width | number | 600 | Width (px). | | height | number | 400 | Height (px). | | variant | "shimmer" \| "pulse" \| "none" | auto | Animation variant (auto-detects prefers-reduced-motion). | | className | string | — | Additional CSS class. |


Hooks

Core Hooks

usePlaybackController(options)

Headless state machine for timeline playback. Powers the PlaybackController component.

const controller = usePlaybackController({
  timelines: ast.timelines,
  transitions: ast.transitions,
});

// controller.play(), controller.pause(), controller.stepForward(), ...
// controller.currentStateName, controller.stepIndex, controller.totalSteps, ...

Options — UsePlaybackControllerOptions:

| Option | Type | Description | |--------|------|-------------| | timelines | TimelineBlock[] | Timeline blocks from the parsed AST. | | transitions | TransitionBlock[] | Transition blocks (for default inference). | | managerOptions | object | Custom TimelineManager options. |

Returns — PlaybackControllerState:

| Field | Type | Description | |-------|------|-------------| | playbackState | PlaybackState | Current state (idle, playing, paused). | | stepIndex | number | Current step (0-based). | | totalSteps | number | Total steps in the active timeline. | | stateLabels | string[] | Ordered state names. | | currentStateName | string | Current state name. | | loop | boolean | Whether looping is enabled. | | speed | SpeedOption | Current speed (0.5, 1, or 2). | | activeTimelineName | string \| null | Active timeline name. | | timelineNames | string[] | All available timelines. | | hasTimelines | boolean | Whether any timelines exist. | | play | () => void | Start/resume playback. | | pause | () => void | Pause playback. | | togglePlayPause | () => void | Toggle play/pause. | | stepForward | () => void | Go to next state. | | stepBack | () => void | Go to previous state. | | goToStep | (index: number) => void | Jump to a specific step. | | toggleLoop | () => void | Toggle looping. | | setSpeed | (speed: SpeedOption) => void | Set speed multiplier. | | setTimeline | (name: string) => void | Switch timeline. |


useRecoveryParse(source, maxErrors?)

Parses DSL source with error recovery — collects ALL parse errors (not just the first) and produces a partial AST when possible.

const { ast, errors, isComplete, skippedLines } = useRecoveryParse(dslSource);

if (ast) {
  // Render the (possibly partial) diagram
}
if (errors.length > 0) {
  // Show errors
}

Returns — RecoveryParseState:

| Field | Type | Description | |-------|------|-------------| | ast | DiagramAST \| null | Narrowed AST (null if header failed). | | errors | RecoveryParseError[] | All collected parse errors. | | isComplete | boolean | Whether parsing succeeded without errors. | | skippedLines | number[] | Line numbers skipped during recovery. |


useTimelinePlayback(diagram)

Lower-level timeline hook wrapping the headless TimelineManager for React.

const {
  timelineNames, activeTimeline, hasTimelines,
  playbackState, currentState,
  play, pause, stepForward, stepBack, goToState,
} = useTimelinePlayback(diagram);

useBlockCollapseState(allBlockIds)

Manages collapse/expand state for block diagram nodes.

import { useBlockCollapseState, collectAllBlockIds } from "@immediate-diagram/react";

const blockIds = collectAllBlockIds(blockAst);
const { collapsedBlocks, toggleBlock, expandAll, collapseAll, isCollapsed } =
  useBlockCollapseState(blockIds);

Returns — UseBlockCollapseStateReturn:

| Field | Type | Description | |-------|------|-------------| | collapsedBlocks | Set<string> | Currently collapsed block IDs. | | toggleBlock | (id: string) => void | Toggle a block. | | expandAll | () => void | Expand all blocks. | | collapseAll | () => void | Collapse all blocks. | | isCollapsed | (id: string) => boolean | Check if a block is collapsed. |


Viewport Hooks

useViewportTransform(options)

Manages zoom/pan state for interactive diagram viewers. Uses direct DOM manipulation for 60fps performance during continuous interactions.

const {
  containerRef, viewportState,
  zoomIn, zoomOut, zoomTo, fitToView, resetZoom,
  panBy, resetPan,
} = useViewportTransform({
  svgWidth: 800,
  svgHeight: 600,
  minScale: 0.1,
  maxScale: 10,
});

return <div ref={containerRef}>{/* SVG content */}</div>;

Key options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | svgWidth | number | (required) | SVG content width (from viewBox). | | svgHeight | number | (required) | SVG content height (from viewBox). | | minScale | number | 0.1 | Minimum zoom scale. | | maxScale | number | 10 | Maximum zoom scale. | | zoomStep | number | 0.1 | Scale delta per wheel tick. | | enabled | boolean | true | Enable/disable interactions. |

Returns — UseViewportTransformReturn:

| Field | Type | Description | |-------|------|-------------| | containerRef | RefObject<HTMLDivElement> | Attach to the SVG container. | | viewportState | ViewportState | Current { scale, translateX, translateY }. | | zoomIn / zoomOut | () => void | Zoom step functions. | | zoomTo | (scale: number) => void | Zoom to specific scale. | | fitToView | () => void | Fit diagram to container. | | resetZoom | () => void | Reset to 1:1 centered. | | panBy | (dx, dy) => void | Pan by pixel delta. | | resetPan | () => void | Reset pan to origin. | | applyTransformToDOM | () => void | Apply current state to DOM (for gesture hooks). | | syncToReactState | () => void | Sync DOM state to React (call on gesture end). |


usePinchZoom(options)

Multi-touch gesture support for mobile/tablet. Handles single-finger drag (pan) and two-finger pinch (zoom).

usePinchZoom({
  containerRef,
  viewportStateRef: transform.viewportStateRef,
  applyTransform: transform.applyTransformToDOM,
  minScale: 0.1,
  maxScale: 10,
  onGestureEnd: transform.syncToReactState,
});

This hook is used internally by InteractiveDiagramViewer and typically doesn't need to be used directly.


useViewportKeyboard(options)

Keyboard shortcuts for diagram viewport navigation:

| Key | Action | |-----|--------| | + / = | Zoom in | | - | Zoom out | | 0 | Reset zoom to 100% | | Arrow keys | Pan (50px step) | | Shift + Arrow | Pan fast (200px step) | | Home | Fit to view | | End | Reset pan |

Used internally by InteractiveDiagramViewer.


Utility Hooks

useDebouncedValue(value, delayMs?)

Debounces a value change. Returns the last stable value after delayMs of inactivity.

const debouncedSource = useDebouncedValue(liveSource, 200);
// debouncedSource updates 200ms after the last liveSource change

useThemePreference()

Manages light/dark theme preference with localStorage persistence and system preference detection.

const { theme, toggleTheme, setTheme, isUserSet } = useThemePreference();

Returns — ThemePreference:

| Field | Type | Description | |-------|------|-------------| | theme | "light" \| "dark" | Active theme. | | toggleTheme | () => void | Toggle and persist. | | setTheme | (theme) => void | Set explicitly and persist. | | isUserSet | boolean | Whether the user explicitly chose (vs. system default). |


usePageTheme()

Reactively tracks the page-level data-theme attribute on <html> via a shared singleton MutationObserver.

const pageTheme = usePageTheme(); // "light" | "dark"

useReducedMotion()

Detects prefers-reduced-motion: reduce media query. Re-renders on preference change.

const prefersReducedMotion = useReducedMotion();
// true when the user has enabled reduced motion in OS settings

useLazyRender(ref, options?)

IntersectionObserver-based lazy rendering. The target element renders a placeholder until it scrolls into view.

const containerRef = useRef<HTMLDivElement>(null);
const { hasBeenVisible } = useLazyRender(containerRef, { rootMargin: "100px" });

return (
  <div ref={containerRef}>
    {hasBeenVisible ? <DiagramViewer ast={ast} /> : <DiagramSkeleton />}
  </div>
);

Returns — UseLazyRenderReturn:

| Field | Type | Description | |-------|------|-------------| | hasBeenVisible | boolean | Latching — once true, stays true. | | isIntersecting | boolean | Currently in viewport (dynamic). |


useCopyDiagramAsImage(options)

Copy a rendered SVG diagram to clipboard as PNG.

const { copyAsImage, status, errorMessage } = useCopyDiagramAsImage({
  containerRef: diagramRef,
  pixelRatio: 2,
  backgroundColor: "#ffffff",
});

Status values: "idle" | "copying" | "success" | "fallback-download" | "error"


useSplitPane(options?)

Resizable split-pane layout for editor/preview layouts.

const { ratio, isDragging, handlePointerDown, setRatio } = useSplitPane({
  initialRatio: 0.5,
  minRatio: 0.2,
  maxRatio: 0.8,
});

return (
  <div style={{ display: "flex" }}>
    <div style={{ width: `${ratio * 100}%` }}>Editor</div>
    <div onPointerDown={handlePointerDown} style={{ cursor: "col-resize", width: 8 }} />
    <div style={{ width: `${(1 - ratio) * 100}%` }}>Preview</div>
  </div>
);

Utilities

Exported utility functions for advanced use cases:

Block Collapse

import {
  collapseBlocks,        // Transform AST to collapse specified blocks
  collectAllBlockIds,    // Get all block IDs from a block diagram AST
  collectBlockChildCounts, // Count children per block
  getBlockId,            // Get the canonical ID for a block
  slugifyBlockLabel,     // Slugify a block label for use as an ID
} from "@immediate-diagram/react";

Syntax Highlighting

import { tokenizeLine, tokensToHtml } from "@immediate-diagram/react";

const tokens = tokenizeLine("A --> B");
const html = tokensToHtml(tokens);
// <span class="imd-syn-identifier">A</span> <span class="imd-syn-arrow">--></span> ...

SVG Processing

import {
  extractSvgDimensions,  // Extract width/height from SVG viewBox
  injectViewportGroup,   // Inject a <g> wrapper for zoom/pan transforms
  sanitizeSvg,           // Sanitize SVG through DOMPurify
  DOMPURIFY_SVG_CONFIG,  // DOMPurify config optimized for SVG diagrams
} from "@immediate-diagram/react";

Built-in Examples

import { EXAMPLE_DIAGRAMS, EXAMPLE_TYPES } from "@immediate-diagram/react";

// 15 pre-built example diagrams covering all 7 diagram types
EXAMPLE_DIAGRAMS.forEach(({ id, type, title, description, source, tags }) => {
  console.log(`${type}: ${title}`);
});

Theming

Provider-Level Theme

Set the theme for all diagrams in the tree:

// Preset
<DiagramProvider theme="dark">

// Partial custom overrides (merged with defaults)
<DiagramProvider theme={{ name: "custom", background: "#1a1a2e", nodeStroke: "#e94560" }}>

Per-Diagram Theme via DSL

Individual diagrams can declare their theme in the DSL:

flowchart
theme dark
A --> B --> C

When DiagramProvider has no explicit theme prop, the DSL theme directive takes effect.

System Theme Detection

import { useThemePreference, usePageTheme } from "@immediate-diagram/react";

// Full preference management (with localStorage persistence)
const { theme, toggleTheme } = useThemePreference();

// Read-only page theme tracking (watches <html data-theme="...">)
const pageTheme = usePageTheme();

Examples

Basic Rendering

import { ImmediateDiagram } from "@immediate-diagram/react";

function BasicExample() {
  return (
    <ImmediateDiagram
      dsl={`flowchart
  Start("Start") --> Check{"Ready?"}
  Check -->|Yes| Deploy["Deploy 🚀"]
  Check -->|No| Wait("Wait") --> Check`}
    />
  );
}

Dark Theme

<ImmediateDiagram
  dsl={`sequence
  Client ->> Server: POST /api/login
  Server ->> DB: SELECT user
  DB -->> Server: User record
  Server -->> Client: 200 JWT token`}
  theme="dark"
/>

Interactive Viewer with Zoom/Pan

<ImmediateDiagram
  dsl={`architecture
  group "Frontend" {
    React["React App"]
    Redux["State"]
  }
  group "Backend" {
    API["REST API"]
    DB[("Database")]
  }
  React --> API
  Redux --> React
  API --> DB`}
  interactive
  showZoomControls
  initialZoom="fit"
/>

Timeline Playback

<ImmediateDiagram
  dsl={`pie "Market Share"
  "Chrome": 65
  "Safari": 19
  "Firefox": 4
  "Edge": 4
  "Other": 8

  @state "2023" {
    "Chrome": 63
    "Safari": 20
    "Firefox": 5
  }

  @state "2024" {
    "Chrome": 65
    "Safari": 19
    "Firefox": 4
  }

  @timeline "growth" {
    default --> "2023" --> "2024"
  }`}
  showControls
  autoPlay
/>

Custom Viewer with Hooks

Build a completely custom diagram viewer using the lower-level hooks:

import { parse, narrowDiagram } from "@immediate-diagram/parser";
import { renderDiagram } from "@immediate-diagram/renderer";
import {
  DiagramProvider,
  useDiagramContext,
  useEffectiveTheme,
} from "@immediate-diagram/react";

function CustomViewer({ source }: { source: string }) {
  const { measureText, layoutEngine, fontsReady } = useDiagramContext();
  const doc = parse(source);
  const ast = narrowDiagram(doc);
  const theme = useEffectiveTheme(ast.metadata);

  if (!fontsReady) return <p>Loading fonts…</p>;

  const positioned = layoutEngine.layout(ast, { measureText });
  const svg = renderDiagram(positioned, { theme });

  return <div dangerouslySetInnerHTML={{ __html: svg }} />;
}

function App() {
  return (
    <DiagramProvider theme="dark">
      <CustomViewer source="flowchart\n  A --> B" />
    </DiagramProvider>
  );
}

Multiple Diagrams on One Page

Share a single provider for efficient resource reuse:

import { DiagramProvider, DiagramViewerFromSource } from "@immediate-diagram/react";

const diagrams = [
  "flowchart\n  A --> B --> C",
  "pie\n  \"React\": 60\n  \"Vue\": 25\n  \"Angular\": 15",
  "venn\n  {A} \"Frontend\"\n  {B} \"Backend\"\n  {A,B} \"Fullstack\"",
];

function Dashboard() {
  return (
    <DiagramProvider>
      {diagrams.map((dsl, i) => (
        <DiagramViewerFromSource key={i} source={dsl} lazy />
      ))}
    </DiagramProvider>
  );
}

Use lazy to defer layout computation for off-screen diagrams.

Error Handling

import { ImmediateDiagram } from "@immediate-diagram/react";

<ImmediateDiagram
  dsl={userInput}
  onError={(error, info) => {
    console.error("Render error:", error);
    reportToSentry(error, info);
  }}
  errorFallback={(error, reset) => (
    <div style={{ padding: 16, color: "red" }}>
      <p>Failed to render: {error.message}</p>
      <button onClick={reset}>Retry</button>
    </div>
  )}
  renderError={(error, source) => (
    <pre style={{ color: "orange" }}>
      Parse error at line {error.location?.start?.line}: {error.message}
    </pre>
  )}
/>

Lazy Loading

For pages with many diagrams, enable lazy rendering to avoid computing layout for off-screen content:

<DiagramProvider>
  {allDiagrams.map((ast, i) => (
    <DiagramViewer
      key={i}
      ast={ast}
      lazy
      lazyRootMargin="200px"  // Start rendering 200px before viewport entry
    />
  ))}
</DiagramProvider>

Copy to Clipboard

import { useRef } from "react";
import { DiagramProvider, DiagramViewer, CopyImageButton } from "@immediate-diagram/react";

function CopyableViewer({ ast }) {
  const containerRef = useRef<HTMLDivElement>(null);

  return (
    <DiagramProvider>
      <div ref={containerRef} style={{ position: "relative" }}>
        <DiagramViewer ast={ast} />
        <CopyImageButton
          containerRef={containerRef}
          position="top-right"
          pixelRatio={2}
          backgroundColor="#ffffff"
        />
      </div>
    </DiagramProvider>
  );
}

CSS (Optional)

Import the optional stylesheet for enhanced visual styling:

import "@immediate-diagram/react/styles.css";

All components work without this import (graceful degradation). The CSS provides:

  • .imd-syn-* — Syntax highlighting colors for IMD DSL (light + dark themes)
  • .theme-toggle — Theme toggle button styling with hover effects
  • .toast-* — Toast notification animations and variant colors
  • .loading-spinner-* — Animated loading indicator
  • @media (prefers-reduced-motion) — Accessibility: disables animations when user prefers reduced motion

Migration from @immediate-diagram/web

If you're currently importing diagram components from @immediate-diagram/web, migration is straightforward:

1. Install the react package

npm install @immediate-diagram/react

2. Update imports

- import { ImmediateDiagram } from "@immediate-diagram/web";
+ import { ImmediateDiagram } from "@immediate-diagram/react";

- import { DiagramViewer, DiagramProvider } from "@immediate-diagram/web";
+ import { DiagramViewer, DiagramProvider } from "@immediate-diagram/react";

- import "@immediate-diagram/web/styles.css";
+ import "@immediate-diagram/react/styles.css";

3. API compatibility

All component props, hook signatures, and context APIs are identical. The react package is a direct extraction of the diagram components from the web package — no breaking changes.

4. What stays in @immediate-diagram/web

The web package retains page-level concerns: routing, authentication, dashboard, editor UI. The react package contains all reusable diagram rendering components, hooks, and utilities.


TypeScript Support

This package is written in TypeScript and ships .ts source files. All components, hooks, props interfaces, and utility types are fully typed and exported.

Key exported types:

import type {
  // Component props
  ImmediateDiagramProps,
  ImmediateDiagramRef,
  DiagramViewerProps,
  DiagramViewerFromSourceProps,
  InteractiveDiagramViewerProps,
  PlaybackControllerProps,
  ZoomControlsProps,
  CopyImageButtonProps,
  DiagramTooltipProps,
  ThemeToggleProps,
  DiagramWrapperProps,
  DiagramErrorBoundaryProps,
  RecoveryErrorPanelProps,
  LoadingSpinnerProps,
  ConfirmDialogProps,
  ToastMessage,
  ToastContainerProps,
  TooltipData,

  // Context
  DiagramProviderProps,
  DiagramContextValue,
  DiagramSkeletonProps,
  SkeletonVariant,

  // Hook return types
  PlaybackControllerState,
  SpeedOption,
  RecoveryParseState,
  UseBlockCollapseStateReturn,
  UseViewportTransformReturn,
  ViewportState,
  UseLazyRenderReturn,
  UseSplitPaneReturn,
  ThemePreference,
  UseTimelinePlaybackReturn,
  CopyImageStatus,
  UseCopyDiagramAsImageReturn,

  // Utility types
  SyntaxToken,
  ExampleDiagram,
  ExampleTypeFilter,
  ZoomControlsPosition,
  CopyImageButtonPosition,
  Point,
} from "@immediate-diagram/react";

Browser Support

  • Chrome / Edge: 90+
  • Firefox: 90+
  • Safari: 15+
  • Mobile Safari: 15+
  • Chrome Android: 90+

Requires:

  • IntersectionObserver (for lazy rendering — falls back to eager rendering)
  • ResizeObserver (for fit-to-view calculations — falls back gracefully)
  • Canvas API (for text measurement — falls back to heuristic estimation)
  • Clipboard API (for copy-to-clipboard — falls back to file download)

Troubleshooting

"Cannot find module '@immediate-diagram/react'"

Ensure you've installed the package and its peer dependencies:

npm install @immediate-diagram/react react react-dom

Diagram renders with wrong text sizing

Text measurement depends on the Canvas API and font loading. If custom fonts haven't loaded yet, the component shows a skeleton placeholder. If text sizing is consistently wrong, check that the fontFamily prop on DiagramProvider matches the fonts used in your app.

Blank diagram (no SVG output)

  1. Check the browser console for parse errors.
  2. Verify the DSL syntax — use DiagramViewerFromSource which shows parse errors automatically.
  3. Ensure DiagramProvider wraps your viewer components (not needed for ImmediateDiagram).

Interactive zoom/pan not working

  • Ensure interactive={true} is set on ImmediateDiagram or use InteractiveDiagramViewer.
  • For keyboard shortcuts, the diagram container must have focus (tabIndex={0} is set automatically).
  • On mobile, touch events require the diagram container to be large enough to interact with.

DOMPurify errors in SSR / Node.js

DOMPurify requires a DOM environment. In SSR contexts, either:

  1. Set sanitize={false} (the default) — the rendering pipeline already escapes user text.
  2. Use isomorphic-dompurify in your SSR setup.

Timeline playback controls not showing

The playback controller only appears when the diagram has @state or @timeline blocks. With 0–1 states, no controls are rendered. With 2 states, a compact toggle appears. With 3+ states, the full controller is shown.

Theme mismatch between page and diagram

Use DiagramProvider theme="dark" to match your page theme, or let diagrams control their own theme via the DSL theme dark directive. The DiagramWrapper automatically adds a visual boundary when themes differ.

Performance with many diagrams

For pages with 20+ diagrams, enable lazy rendering:

<DiagramViewer ast={ast} lazy lazyRootMargin="200px" />

This defers layout computation until each diagram scrolls into view.


API Reference

All public APIs are documented with JSDoc in the source files. The exports are organized into four categories:

| Category | Exports | |----------|---------| | Components | ImmediateDiagram, DiagramViewer, DiagramViewerFromSource, InteractiveDiagramViewer, PlaybackController, ZoomControls, CopyImageButton, DiagramTooltip, ThemeToggle, ToastContainer, ConfirmDialog, DiagramWrapper, DiagramErrorBoundary, RecoveryErrorPanel, LoadingSpinner, Button | | Context | DiagramProvider, DiagramSkeleton, useDiagramContext, useEffectiveTheme, useDiagramTheme, useFontsReady, useLayoutEngine, useMeasureText, checkFontAvailable, extractPrimaryFont | | Hooks | usePlaybackController, useRecoveryParse, useTimelinePlayback, useBlockCollapseState, useViewportTransform, usePinchZoom, useViewportKeyboard, useDebouncedValue, useThemePreference, usePageTheme, useReducedMotion, useLazyRender, useCopyDiagramAsImage, useSplitPane, useToast | | Utilities | collapseBlocks, collectAllBlockIds, collectBlockChildCounts, getBlockId, slugifyBlockLabel, tokenizeLine, tokensToHtml, extractSvgDimensions, injectViewportGroup, sanitizeSvg, DOMPURIFY_SVG_CONFIG, clampScale, computeCursorZoom, computeFitToView, touchDistance, touchMidpoint, computePinchScale, isClipboardWriteSupported, downloadBlob, rasterizeSvgToBlob, postProcessCollapseIndicators, EXAMPLE_DIAGRAMS, EXAMPLE_TYPES, SPEED_OPTIONS, PAN_STEP, PAN_FAST_STEP, COLLAPSED_BLOCK_WIDTH, COLLAPSED_BLOCK_HEIGHT |

For detailed type information, see the TypeScript source at src/index.ts and the individual module files.


License

MIT