Web component
<sfn-diagram> is a custom element shipped from the sfn-diagram/element subpath — no
extra package. It works anywhere custom elements do: Vue, Svelte, Solid, Angular, Astro,
Hono, plain HTML/htmx, and React 19+. It renders in light DOM (no shadow root), so
page CSS reaches the SVG, and it degrades to plain server-rendered markup when there’s
no client-side JavaScript at all.
npm i sfn-diagrampnpm add sfn-diagramyarn add sfn-diagrambun add sfn-diagramRegistering it
Section titled “Registering it”Import sfn-diagram/element/auto for the zero-config default — it registers
<sfn-diagram> as a side effect:
<script type="module" src="sfn-diagram/element/auto"></script>
<sfn-diagram interactive theme="dark"></sfn-diagram>
<script type="module"> document.querySelector('sfn-diagram').definition = myAslDefinition;</script>Import sfn-diagram/element directly (no /auto) and call defineSfnDiagram yourself
for a different tag name — the custom element spec allows registering one class under
only one tag name, ever, so only one of the two should run per page:
import { defineSfnDiagram } from 'sfn-diagram/element';
defineSfnDiagram({ name: 'my-diagram' }); // <my-diagram> instead of <sfn-diagram>Attributes and properties
Section titled “Attributes and properties”| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
definition |
definition |
AslDefinition | string |
— (required) | Attribute accepts a JSON string; the property also accepts an object |
| — | history |
ExecutionHistoryInput |
— | Property-only — objects don’t survive an attribute round-trip. Renders an execution overlay when set |
format |
format |
'svg' | 'mermaid' |
'svg' |
|
layout |
layout |
'TB' | 'LR' | 'RL' | 'BT' |
'TB' |
|
theme |
theme |
'light' | 'dark' | CustomTheme |
'light' |
Attribute accepts 'light'/'dark' only; set the property for a CustomTheme object |
interactive |
interactive |
boolean |
false |
Enables pan/zoom, state search, the minimap, and the detail panel for a clicked state or edge |
An embedded viewer sits inside a page that scrolls, so it doesn’t take over the mouse wheel or touch gestures until you engage with it — click or tap the diagram, or move keyboard focus into it — and gives them back once you click elsewhere on the page. The one exception is Ctrl+wheel, which is also what a trackpad pinch sends: pinching over the diagram zooms the diagram at any time, engaged or not, since that gesture is never an attempt to scroll the page.
Failure to parse or validate definition dispatches a bubbling sfn-error event
(CustomEvent<Error>) and renders nothing:
document.querySelector('sfn-diagram').addEventListener('sfn-error', (event) => { console.error(event.detail); // Error});Framework usage
Section titled “Framework usage”Every framework in Use with your framework can set properties on
a custom element directly — React 19+, Vue, Svelte, Solid, and Angular all pass
custom-elements-everywhere’s full test suite,
so .definition = asl (not the definition attribute) works the same way it would on
any other DOM element:
// React 19+ — no wrapper needed, unlike React 18 (use sfn-diagram-react there instead)<sfn-diagram ref={(el) => { if (el) el.definition = asl; }} interactive /><!-- Vue 3 --><sfn-diagram :definition="asl" interactive /><!-- Svelte 5 --><sfn-diagram bind:this={el} interactive />Server-rendered, zero client JS
Section titled “Server-rendered, zero client JS”Omit definition and give the element a pre-rendered <svg> child instead — from
generateSvg() run at build or request time — and it’s left untouched:
---import { generateSvg } from 'sfn-diagram';const { svg } = generateSvg({ aslDefinition: definition });---
<sfn-diagram set:html={svg} />Adding interactive on top of server-rendered markup still upgrades it with pan/zoom
and search once the element’s JavaScript loads — the detail panel is the one exception,
since it needs the original ASL client-side; set definition alongside the pre-rendered
SVG if you need it too.
Multiple diagrams on one page
Section titled “Multiple diagrams on one page”Every internal id the interactive viewer needs is scoped with [data-sfn="..."]
attributes rather than global ids, and each element’s own SVG marker defs are namespaced
per instance — so two (or two hundred) <sfn-diagram interactive> elements on the same
page don’t collide.
Custom elements manifest
Section titled “Custom elements manifest”A custom-elements.json is
published alongside the package (sfn-diagram’s customElements field), for editor
autocomplete, JSX types, and generated framework wrappers via tools like
wc-toolkit.