@workflowbuilder/sdk
v2.2.0
Published
React SDK for Workflow Builder — embed a visual, flow-based workflow editor in your app.
Downloads
15,196
Maintainers
Readme
@workflowbuilder/sdk
React SDK for Workflow Builder. Embed a visual, flow-based workflow editor in your app.
- Docs: https://www.workflowbuilder.io/docs/overview/
- Live demo: https://app.workflowbuilder.io/
- Source: https://github.com/synergycodes/workflowbuilder
- Changelog: CHANGELOG.md
What you get
An embeddable React SDK that ships a complete workflow editor. One compound component (<WorkflowBuilder.Root>) is the embed point. Behind it: a plugin architecture, three persistence strategies, a JsonForms-driven schema layer, i18n, theming tokens, and a diagram-template system. You bring node types and a save strategy. The SDK handles canvas, palette, connections, properties, and persistence.
Typical fit:
- Visual editors for AI agent workflows (LLM chains, tool calls, decision branches)
- Drag-and-drop node-based programming UIs
- ETL or data-pipeline designers
- BPMN-style process editors
- Any flow editor where you want JsonForms-driven node properties plus a swappable persistence layer
Battle-tested in production by teams shipping AI workflow products.
Install
npm install @workflowbuilder/sdk \
react react-dom \
@xyflow/react \
@jsonforms/core @jsonforms/react \
i18next react-i18next i18next-browser-languagedetector \
immer zustandThe SDK ships its non-peer dependencies bundled in dist/. React, xyflow, JsonForms, i18next, immer, and zustand are declared as peerDependencies so your app and the SDK share a single copy of each. See Peer dependencies below.
Quick start
import { WorkflowBuilder } from '@workflowbuilder/sdk';
import '@workflowbuilder/sdk/style.css';
export function App() {
return <WorkflowBuilder.Root name="my-workflow" />;
}The editor renders with the default layout (top bar, palette, canvas, properties panel) and persists state to localStorage automatically (the default integration strategy). The palette is empty until you pass nodeTypes — see <Root> props, the Add a custom node type guide, and the node schemas reference for how to design the data and UI schemas.
Copy-paste-ready node implementations live in the source repo at apps/demo/src/app/data/nodes/. Each folder is a self-contained node type with 4 files (<name>.ts, default-properties-data.ts, schema.ts, uischema.ts). Available examples: trigger, action, decision, conditional, delay, notification, multi-port, ai-agent — each documented on the built-in nodes reference. Drop the folder into your app, import the node definition, and pass it to nodeTypes.
Compose a custom layout
Pass subcomponents as children to skip the default layout and place each part where you want it:
<WorkflowBuilder.Root nodeTypes={myNodeTypes}>
<header>
<WorkflowBuilder.TopBar />
</header>
<aside>
<WorkflowBuilder.Palette />
</aside>
<main>
<WorkflowBuilder.Canvas />
</main>
<aside>
<WorkflowBuilder.PropertiesPanel />
</aside>
</WorkflowBuilder.Root>To add custom overlays alongside the default layout, mount it explicitly:
<WorkflowBuilder.Root nodeTypes={myNodeTypes}>
<WorkflowBuilder.DefaultLayout />
<MyToastBanner />
</WorkflowBuilder.Root>Each subcomponent is also exported under a named alias (WorkflowBuilderTopBar, WorkflowBuilderPalette, WorkflowBuilderCanvas, WorkflowBuilderPropertiesPanel, WorkflowBuilderDefaultLayout) for consumers who prefer the classic style.
If you omit <WorkflowBuilder.TopBar />, use useWorkflowBuilderActions() to trigger save / import / export / settings / read-only / theme / layout-direction from your own controls.
<WorkflowBuilder.Root> props
| Prop | Type | Description |
| ------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| nodeTypes | PaletteItemOrGroup[] | Node type definitions. Appear in the palette and drive validation. Must be a stable reference — declare at module scope or memoize. |
| nodeTemplates | WorkflowBuilderNodeTemplates | Per-node-type custom renderers. Map of data.type → React component, overriding the default node renderer for that type. Stable reference required (same as nodeTypes). |
| edgeTemplates | WorkflowBuilderEdgeTemplates | Per-edge-type custom renderers. Map of edge.type → React component taking ReactFlow EdgeProps, overriding the built-in labelEdge. Unregistered types fall back to the default edge. Stable reference required (same as nodeTypes). |
| diagramTemplates | TemplateModel[] | Diagram templates available in the template selector. Stable reference required (same as nodeTypes). |
| plugins | WorkflowBuilderPlugin[] | Functions registering decorators. Synchronous, executed once. |
| jsonForm | WorkflowBuilderJsonFormConfig | Custom JsonForms renderers, cells, translations. |
| integration | WorkflowBuilderIntegration | Data source / sink. Defaults to localStorage. |
| name | string | Workflow name shown in the header. |
| layoutDirection | 'DOWN' \| 'RIGHT' | Initial flow direction. |
| initialNodes | WorkflowBuilderNode[] | Starting diagram nodes. |
| initialEdges | WorkflowBuilderEdge[] | Starting diagram edges. |
| isValidConnection | WorkflowBuilderIsValidConnection | Validate connections as the user draws them. See Connection validation. Stable reference required. |
| reactFlowProps | WorkflowBuilderReactFlowProps | Advanced escape hatch for the underlying ReactFlow canvas. See Advanced: ReactFlow props. Treat as static config (runtime value changes may not apply immediately). |
| children | ReactNode | Custom layout. Omit for the default floating-overlay layout (top bar, palette, canvas, properties panel). See Compose a custom layout. |
Exact prop types come from the auto-generated WorkflowBuilderRootProps reference. For how and when to reach for each prop, see Configuring the editor.
Persistence
Pick one of three strategies via the integration prop.
| Strategy | Source / sink | When to use |
| -------------- | ----------------------------------------------------------- | ---------------------------- |
| localStorage | Browser localStorage | Prototyping, quick starts |
| api | GET / POST to user-provided endpoints | Backend-managed persistence |
| props | onDataSave callback + initialNodes/initialEdges props | Host-app-managed persistence |
<WorkflowBuilder.Root
integration={{
strategy: 'api',
endpoints: { load: '/api/load', save: '/api/save' },
}}
/>Full guide: Persistence strategies on the docs site.
Plugins
The SDK exposes three extension points. Pass plugin functions to the plugins prop:
- Component decorators (
registerComponentDecorator). Inject UI into named slots:OptionalAppBarControls,OptionalAppBarTools,OptionalAppChildren,OptionalEdgeProperties,OptionalFooterContent,OptionalHooks(invisible provider slot),OptionalNodeContent(receivesnodeId). - Function decorators (
registerFunctionDecorator). Observe or wrap SDK internals (e.g.trackFutureChange) before / after / wrapping the call. - Translations (
registerPluginTranslation). Extend i18next keys with plugin-specific strings.
import { WorkflowBuilder, type WorkflowBuilderPlugin, registerComponentDecorator } from '@workflowbuilder/sdk';
import { MyToolbarButton } from './my-toolbar-button';
const myPlugin: WorkflowBuilderPlugin = () => {
registerComponentDecorator('OptionalAppBarControls', {
content: MyToolbarButton,
name: 'MyPlugin',
});
};
export function App() {
return <WorkflowBuilder.Root plugins={[myPlugin]} />;
}Plugins are synchronous. If you need async work (config fetch, WASM load, feature flag lookup), await it outside the SDK and construct the plugin around the resolved value before passing it to <Root>.
Full guide: Build a plugin.
Connection validation
isValidConnection decides whether a dragged connection is allowed. Return false to reject it: no edge is created, no flicker. It receives the resolved sourceNode / targetNode (plus the raw connection), so you branch on node data without reaching into the store.
import { WorkflowBuilder, type WorkflowBuilderIsValidConnection } from '@workflowbuilder/sdk';
// Module scope keeps the reference stable.
const isValidConnection: WorkflowBuilderIsValidConnection = ({ sourceNode, targetNode }) =>
!(sourceNode.data.type === 'start' && targetNode.data.type === 'start');
export function App() {
return <WorkflowBuilder.Root isValidConnection={isValidConnection} />;
}Validates interactive drags only, not programmatic edge writes (templates, paste, setStoreEdges). Fail-open: if an endpoint can't be resolved to a node, the connection is allowed.
Advanced: ReactFlow props
reactFlowProps forwards extra props to the underlying ReactFlow canvas for things the SDK doesn't expose directly (zoom limits, key codes, onNodeClick, performance flags, …).
const reactFlowProps = {
maxZoom: 1.5,
zoomOnDoubleClick: false,
onNodeClick: (_, node) => console.log(node.id),
} satisfies WorkflowBuilderReactFlowProps;
<WorkflowBuilder.Root reactFlowProps={reactFlowProps} />;Props the SDK owns (graph data, the connection / selection / change handlers, type maps, colorMode, …) can't be set here. To observe SDK events use the listener APIs (addNodeChangedListener, …); to theme use Theming. Treat reactFlowProps as static config: runtime value changes may not apply immediately.
Theming
The editor exposes a small set of CSS custom properties for top-level styling. Override them on :root or scope to your app shell:
:root {
--wb-background-color: #fafafa;
--wb-font-family: 'Inter', system-ui, sans-serif;
--wb-transition: 0.15s ease-in;
}Available --wb-* tokens: --wb-background-color, --wb-font-family, --wb-transition, plus scrollbar styling (--wb-scroll-width, --wb-scroll-radius, --wb-scroll-thumb-color, --wb-scroll-thumb-hover-color, --wb-scroll-track-color).
Deeper color and spacing customization (palette, semantic UI tokens) goes through the --ax-* token layer from @synergycodes/overflow-ui. Full guide: Design system and customization.
CSS — global resets
@workflowbuilder/sdk/style.css contains a small set of global resets, including:
body {
margin: 0;
background-color: var(--wb-background-color);
overflow: hidden;
}
html,
body,
#root {
height: 100vh;
}The rules ship inside @layer reset (the lowest precedence in the SDK's cascade layer
order), so any consumer-level rule on body / html / #root wins automatically — no
specificity hacks or !important needed. Two things to be aware of:
overflow: hiddenon<body>— the editor expects a full-viewport canvas with no page-level scrollbar. If you embed the editor inside a scrolling page, overridebody { overflow: auto; }(or scope the SDK to a sized container with its ownoverflow: hidden) and let the editor's own viewport handle pan/zoom.background-color: var(--wb-background-color)— the body picks up the SDK's background token. If you don't ship the SDK style sheet but render<WorkflowBuilder.Root />, the token is undefined and the body falls back to transparent. Either ship the CSS or set--wb-background-coloryourself.
If you don't want the body resets at all, import the SDK before your own global
stylesheet so your rules land in the unlayered cascade above @layer reset.
Single-instance constraint
Mount one <WorkflowBuilder.Root> per page. Plugin / JsonForms / i18n registries and the useStore.{getState,setState,subscribe} facade all share a module-level singleton, so two Roots on the same page would silently clash. If you need multiple "workflows" on one page, render them sequentially (mount → save → unmount → mount next).
TypeScript
dist/index.d.ts ships with all required types inlined: icon names (WBIcon), domain types (WorkflowBuilderNode, WorkflowBuilderEdge, NodeData, DiagramModel, PaletteItemOrGroup, TemplateModel, …), plugin contracts, and integration types. No extra @types/* package needed.
Full API reference: https://www.workflowbuilder.io/docs/api/core/workflowbuilder/.
Browser and runtime
- Client-side only. The SDK holds module-level singletons (zustand store identity, i18next instance, immer's frozen-object cache) and uses
i18next-browser-languagedetector. Mount inside'use client'boundaries on Next.js, or behind a client-only wrapper (dynamic(() => import('./Editor'), { ssr: false })) on other SSR frameworks. - Modern browsers. Last two versions of Chrome, Firefox, Safari, Edge.
- React 18 or 19.
Peer dependencies
Install alongside the SDK (ranges declared in peerDependencies):
react,react-dom(^18.0.0 || ^19.0.0)@xyflow/react(^12.0.0)@jsonforms/core,@jsonforms/react(^3.4.0)i18next(^24.0.0),react-i18next(^15.0.0),i18next-browser-languagedetector(^8.0.0)immer(^10.0.0)zustand(^5.0.0)
These are declared as peerDependencies so the consumer app and the SDK share a single
copy of each — required for singletons (zustand store identity, i18next instance,
immer's frozen-object cache).
Support
Built and maintained by Synergy Codes - the engineers behind the SDK shipping workflow tools for 15 years across 20+ industries. Available for integration, customization, UX, back-end logic, and end-to-end delivery.
License
Apache 2.0 — see LICENSE.
