React bindings for Grafloria Diagrams and Grafloria Dashboards — an MIT diagram and dashboard engine: routing, auto-layout, undo, collaboration and dashboard layouts, native in React.
Install
bashnpm install @grafloria/react @grafloria/engine @grafloria/renderer react react-dom @grafloria/element
It expects these alongside it:
@grafloria/engine^0.3.0@grafloria/renderer^0.4.16react^17.0.0 || ^18.0.0 || ^19.0.0react-dom^17.0.0 || ^18.0.0 || ^19.0.0@grafloria/element^0.4.3
Functions
createGrafloriaStore
tsfunction createGrafloriaStore(): GrafloriaStore
GrafloriaCommentPanel
tsfunction GrafloriaCommentPanel(props: GrafloriaCommentPanelProps)
Props
| Name | Type | Default | Description |
|---|---|---|---|
store | CommentStore | ||
options? | CommentPanelOptions | ||
onSelect? | (threadId: string) => void | ||
className? | string | ||
style? | CSSProperties |
Events
onSelect
GrafloriaDashboard
tsfunction GrafloriaDashboard(props: GrafloriaDashboardProps)
Props
| Name | Type | Default | Description |
|---|---|---|---|
views? | DashboardViewSpec[] | Multi-view (tabbed) board. Mutually exclusive with widgets. | |
widgets? | DashboardWidgetSpec[] | Single-view shorthand. | |
options? | Partial<DashboardOptions> | Board options: columns, gap, sizing, rtl, responsive, binder… | |
widgetTypes? | WidgetTypes | Maps a widget kind to the React component that renders it. | |
activeView? | string | The visible view (the tab pattern). Omit for kit-managed. | |
layout? | "grid" | "split" | LIVE SWITCHES — the toolbar toggles as props. Each is applied at mount | |
sizing? | "fit" | "grow" | ||
static? | boolean | Static board: the viewer's mode — no drag, no resize, no handles. | |
onReady? | (handle: DashboardHandle) => void | The typed handle, once the board is live. | |
onLayoutChange? | (change: { viewId: string; widgets: DashboardWidgetSpec[]; }) => void | Mirrors the kit's committed gestures (drag, resize, add, remove). | |
className? | string | ||
style? | CSSProperties | ||
children? | ReactNode |
Events
onReady— The typed handle, once the board is live.onLayoutChange— Mirrors the kit's committed gestures (drag, resize, add, remove).
GrafloriaDiagram
tsfunction GrafloriaDiagram(props: GrafloriaDiagramProps)
Props
| Name | Type | Default | Description |
|---|---|---|---|
spec | RenderSpec | Any kit spec — erDiagram(...), umlDiagram(...), dashboard(...), or DSL text. | |
options? | RenderOptions | Options passed through to the underlying createDiagram. | |
onReady? | (instance: DiagramInstance) => void | ||
className? | string | ||
style? | CSSProperties |
Events
onReady
GrafloriaFlow
tsfunction GrafloriaFlow(props: GrafloriaFlowProps)
Props
| Name | Type | Default | Description |
|---|---|---|---|
nodes? | NodeSpec[] | Controlled nodes. Provide with onNodesChange (see useNodesState). | |
edges? | EdgeSpec[] | Controlled edges. | |
groups? | (GroupModel | GroupSpec)[] | Controlled groups — zones around some nodes (a spec's groups, or the live | |
defaultNodes? | NodeSpec[] | Uncontrolled nodes — the instance owns them from here on. | |
defaultEdges? | EdgeSpec[] | ||
defaultGroups? | (GroupModel | GroupSpec)[] | ||
onNodesChange? | (nodes: NodeModel[]) => void | ||
onEdgesChange? | (edges: LinkModel[]) => void | ||
onSelectionChange? | (change: { nodes: NodeModel[]; edges: LinkModel[]; }) => void | ||
onConnect? | (change: { link: LinkModel; }) => void | ||
onNodeClick? | (change: { node: NodeModel; world: { x: number; y: number; }; }) => void | ||
onEdgeClick? | (change: { edge: LinkModel; world: { x: number; y: number; }; }) => void | ||
onInit? | (instance: DiagramInstance) => void | ||
nodeTypes? | NodeTypes | Custom node components, keyed by node type. | |
theme? | Theme | ||
fitView? | boolean | ||
enablePan? | boolean | ||
enableZoom? | boolean | ||
zoomSensitivity? | number | ||
dragThreshold? | number | ||
readonly? | boolean | ||
minZoom? | number | ||
maxZoom? | number | ||
ssr? | { html: string; snapshot: HydrationSnapshot; } | The renderToStaticSVG() result. Renders server-side, hydrates client-side. | |
layout? | string | { name: string; options?: Record<string, unknown>; } | Declarative auto-layout — any engine registry name ('elk', 'dagre', | |
onLayoutDone? | (result: unknown) => void | Fires after each declarative layout completes. | |
plugins? | boolean | CanvasPluginOptions | Canvas plugins — true mounts minimap + zoom/fit controls + background | |
collab? | GrafloriaCollabOptions | Real-time collaboration: hand in a transport (BroadcastChannelTransport, | |
onCollabReady? | (session: SyncAdapter) => void | The live SyncAdapter, right after join(). | |
comments? | boolean | CommentStore | Anchored comment threads — true creates a store, or pass a shared | |
commentsViewer? | string | Viewer id for a comments: true-created store. | |
rendererConfig? | Record<string, unknown> | Renderer config passthrough (parallelLinks, parallelSpacing, jump styles, …). | |
interaction? | Record<string, unknown> | Interaction config passthrough (portVisibility, enableHelperLines, …). | |
tokenBridge? | unknown | Design-token bridge — adopt the app's shadcn / MUI / Tailwind CSS variables. | |
highlighterConfig? | boolean | Partial<HighlighterConfig> | The outline layer Angular's canvas draws: outlines around the hovered node, | |
highlightConnected? | boolean | HighlightConnectedOptions | Bring the selected nodes' lines forward and fade the rest: true, or | |
className? | string | ||
style? | CSSProperties | ||
children? | ReactNode | Overlays (toolbars, panels). Rendered as siblings of the canvas. |
Events
onNodesChangeonEdgesChangeonSelectionChangeonConnectonNodeClickonEdgeClickonInitonLayoutDone— Fires after each declarative layout completes.onCollabReady— The live SyncAdapter, right afterjoin().
GrafloriaProvider
Wrap anything that needs useGrafloria() outside of <GrafloriaFlow>'s subtree.
tsx<GrafloriaProvider>
<Toolbar /> // useGrafloria() works here…
<GrafloriaFlow … /> // …because the flow publishes its instance to the store
</GrafloriaProvider>
<GrafloriaFlow> also creates its own store when there is no provider, so the
simple single-canvas case needs no wrapper at all.
tsfunction GrafloriaProvider({ children }: GrafloriaProviderProps)
Props
| Name | Type | Default | Description |
|---|---|---|---|
children? | ReactNode |
useEdgesState
Controlled edge state. Mirrors {@link useNodesState}.
tsfunction useEdgesState(initial: EdgeSpec[] = []): EdgesState
useGrafloria
The live DiagramInstance, or null until <GrafloriaFlow> has mounted.
Works from anywhere inside an <GrafloriaProvider> (a toolbar, a minimap, a
sidebar) and from inside <GrafloriaFlow>'s own children.
tsxconst grafloria = useGrafloria();
<button onClick={() => grafloria?.fitView()}>Fit</button>
tsfunction useGrafloria(): DiagramInstance | null
useGrafloriaStore
The nearest store, or null when there is no provider above us.
tsfunction useGrafloriaStore(): GrafloriaStore | null
useNodesState
Controlled node state — the React Flow tuple everyone already knows:
tsxconst [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
<GrafloriaFlow nodes={nodes} onNodesChange={onNodesChange} … />
onNodesChange is what closes the loop: the user drags a node, the ENGINE
moves it, the instance emits nodes:change, <GrafloriaFlow> calls this, and
React state catches up. Without it a controlled <GrafloriaFlow> would snap the
node back on the next render — the classic controlled-component trap.
tsfunction useNodesState(initial: NodeSpec[] = []): NodesState
useOnSelectionChange
Fire a callback whenever the selection changes.
tsxuseOnSelectionChange(({ nodes }) => setInspected(nodes[0] ?? null));
The handler is held in a ref, so passing an inline arrow (the common case) does NOT re-subscribe on every render.
tsfunction useOnSelectionChange(handler: (change: SelectionChange) => void): void
useSelection
The current selection as state (for rendering an inspector panel).
tsfunction useSelection(): SelectionChange
useViewport
The live camera (zoom + world rect) as state — for a minimap or a zoom badge.
tsfunction useViewport(): { zoom: number; x: number; y: number }
Constants
GrafloriaContext
tsconst GrafloriaContext: any
Interfaces
GrafloriaCommentPanelProps
tsinterface GrafloriaCommentPanelProps
Properties
| Name | Type | Default | Description |
|---|---|---|---|
store | CommentStore | ||
options? | CommentPanelOptions | ||
onSelect? | (threadId: string | null) | ||
className? | string | ||
style? | CSSProperties |
GrafloriaDashboardProps
tsinterface GrafloriaDashboardProps
Properties
| Name | Type | Default | Description |
|---|---|---|---|
views? | DashboardViewSpec[] | Multi-view (tabbed) board. Mutually exclusive with widgets. | |
widgets? | DashboardWidgetSpec[] | Single-view shorthand. | |
options? | Partial<DashboardOptions> | Board options: columns, gap, sizing, rtl, responsive, binder… | |
widgetTypes? | WidgetTypes | Maps a widget kind to the React component that renders it. | |
activeView? | string | The visible view (the tab pattern). Omit for kit-managed. | |
layout? | 'grid' | 'split' | LIVE SWITCHES — the toolbar toggles as props. Each is applied at mount (over options) and, when it changes afterwards, through the handle (setLayout / setSizing / setStatic) — no remount, like activeView. 'split' is the DevExpress splitter tree; 'grid' the cell grid. | |
sizing? | 'fit' | 'grow' | ||
static? | boolean | Static board: the viewer's mode — no drag, no resize, no handles. | |
onReady? | (handle: DashboardHandle) | The typed handle, once the board is live. | |
onLayoutChange? | (change: { viewId: string; widgets: DashboardWidgetSpec[] }) | Mirrors the kit's committed gestures (drag, resize, add, remove). | |
className? | string | ||
style? | CSSProperties | ||
children? | ReactNode |
GrafloriaDiagramProps
tsinterface GrafloriaDiagramProps
Properties
| Name | Type | Default | Description |
|---|---|---|---|
spec | RenderSpec | Any kit spec — erDiagram(...), umlDiagram(...), dashboard(...), or DSL text. | |
options? | RenderOptions | Options passed through to the underlying createDiagram. | |
onReady? | (instance: DiagramInstance) | ||
className? | string | ||
style? | CSSProperties |
GrafloriaFlowProps
tsinterface GrafloriaFlowProps
Properties
| Name | Type | Default | Description |
|---|---|---|---|
nodes? | NodeSpec[] | Controlled nodes. Provide with onNodesChange (see useNodesState). | |
edges? | EdgeSpec[] | Controlled edges. | |
groups? | Array<GroupSpec | GroupModel> | Controlled groups — zones around some nodes (a spec's groups, or the live GroupModels of a loaded document). Reconciled like nodes. | |
defaultNodes? | NodeSpec[] | Uncontrolled nodes — the instance owns them from here on. | |
defaultEdges? | EdgeSpec[] | ||
defaultGroups? | Array<GroupSpec | GroupModel> | ||
onNodesChange? | (nodes: NodeModel[]) | ||
onEdgesChange? | (edges: LinkModel[]) | ||
onSelectionChange? | (change: { nodes: NodeModel[]; edges: LinkModel[] }) | ||
onConnect? | (change: { link: LinkModel }) | ||
onNodeClick? | (change: { node: NodeModel; world: { x: number; y: number } }) | ||
onEdgeClick? | (change: { edge: LinkModel; world: { x: number; y: number } }) | ||
onInit? | (instance: DiagramInstance) | ||
nodeTypes? | NodeTypes | Custom node components, keyed by node type. | |
theme? | Theme | ||
fitView? | boolean | ||
enablePan? | boolean | ||
enableZoom? | boolean | ||
zoomSensitivity? | number | ||
dragThreshold? | number | ||
readonly? | boolean | ||
minZoom? | number | ||
maxZoom? | number | ||
ssr? | { html: string; snapshot: HydrationSnapshot } | The renderToStaticSVG() result. Renders server-side, hydrates client-side. | |
layout? | string | { name: string; options?: Record<string, unknown> } | Declarative auto-layout — any engine registry name ('elk', 'dagre', 'force', 'tree', 'grid', 'auto', …) or { name, options }. Re-runs when the prop VALUE changes, never when node data changes. | |
onLayoutDone? | (result: unknown) | Fires after each declarative layout completes. | |
plugins? | boolean | CanvasPluginOptions | Canvas plugins — true mounts minimap + zoom/fit controls + background grid with defaults; an object picks and configures them. | |
collab? | GrafloriaCollabOptions | Real-time collaboration: hand in a transport (BroadcastChannelTransport, WebSocketTransport, MemoryTransport, …) and an actor id — the flow joins a CRDT sync session at mount and leaves on unmount. Fixed for the life of the instance. | |
onCollabReady? | (session: SyncAdapter) | The live SyncAdapter, right after join(). | |
comments? | boolean | CommentStore | Anchored comment threads — true creates a store, or pass a shared CommentStore. Read it back with useGrafloria()?.getCommentStore(). | |
commentsViewer? | string | Viewer id for a comments: true-created store. | |
rendererConfig? | Record<string, unknown> | Renderer config passthrough (parallelLinks, parallelSpacing, jump styles, …). | |
interaction? | Record<string, unknown> | Interaction config passthrough (portVisibility, enableHelperLines, …). | |
tokenBridge? | unknown | Design-token bridge — adopt the app's shadcn / MUI / Tailwind CSS variables. | |
highlighterConfig? | boolean | Partial<HighlighterConfig> | The outline layer Angular's canvas draws: outlines around the hovered node, the selected node, nodes with a validation issue, and valid connection targets. true turns every kind on; an object turns kinds on or off one by one. Off when unset. Live: follows the prop by value. | |
highlightConnected? | boolean | HighlightConnectedOptions | Bring the selected nodes' lines forward and fade the rest: true, or options (depth, stroke, outgoing, dimOpacity). Off when unset. Live: follows the prop by value. | |
className? | string | ||
style? | CSSProperties | ||
children? | ReactNode | Overlays (toolbars, panels). Rendered as siblings of the canvas. |
GrafloriaProviderProps
tsinterface GrafloriaProviderProps
Properties
| Name | Type | Default | Description |
|---|---|---|---|
children? | ReactNode |
GrafloriaStore
The provider + the store behind useGrafloria().
React Flow's ergonomics come from exactly this shape: a <ReactFlowProvider>
that lets a toolbar, a sidebar or a minimap — components that are SIBLINGS of
the canvas, not children of it — reach the live instance. We keep that shape,
but the thing being shared is our framework-agnostic DiagramInstance, so the
provider is a 40-line store and NOT a re-implementation of the diagram.
Why a hand-rolled store rather than useSyncExternalStore: that hook is React
18+, and this package supports React 17–19. Subscribe + useState costs one
extra render on attach and works everywhere.
tsinterface GrafloriaStore
Members
get(): DiagramInstance | null— The live instance, or null before<GrafloriaFlow>has mounted.set(instance: DiagramInstance | null): void— Called by<GrafloriaFlow>on mount/unmount.subscribe(listener: (instance: DiagramInstance | null) => void): () => void— Notified whenever the instance is attached or detached.
NodeProps
Props a custom node component receives. Deliberately React-Flow-shaped.
tsinterface NodeProps<TData = Record<string, unknown>>
Properties
| Name | Type | Default | Description |
|---|---|---|---|
id | string | ||
data | TData | ||
selected | boolean | ||
node | NodeModel | The live engine model — the escape hatch. |
SelectionChange
tsinterface SelectionChange
Properties
| Name | Type | Default | Description |
|---|---|---|---|
nodes | NodeModel[] | ||
edges | LinkModel[] |
WidgetProps
Props a widget component receives — the NodeProps twin for boards.
tsinterface WidgetProps<TData = Record<string, unknown>>
Properties
| Name | Type | Default | Description |
|---|---|---|---|
widget | DashboardWidgetSpec | ||
data | TData |
Types
EdgesState
tstype EdgesState = [
EdgeSpec[],
Dispatch<SetStateAction<EdgeSpec[]>>,
(edges: LinkModel[]) => void,
];
NodesState
What useNodesState hands back — React Flow's tuple, with our types.
tstype NodesState = [
NodeSpec[],
Dispatch<SetStateAction<NodeSpec[]>>,
(nodes: NodeModel[]) => void,
];
NodeTypes
nodeTypes maps a node's type to the component that renders it.
tstype NodeTypes = Record<string, ComponentType<NodeProps<never>>>;
WidgetTypes
tstype WidgetTypes = Record<string, ComponentType<WidgetProps>>;
Also exported from here
These names are documented with the package that defines them, and can be imported from this one too.
- From @grafloria/renderer:
DARK_THEME,DiagramInstance,EdgeSpec,HydrationSnapshot,LIGHT_THEME,NodeSpec,PortSpec,renderToStaticSVG,StaticRenderOptions,StaticRenderResult,Theme
Was this page helpful?