Skip to content

Configuring the editor

Pass node types, integration strategy, plugins, and JsonForms extensions to WorkflowBuilder.Root.

<WorkflowBuilder.Root> is the main entry point of the SDK. Mount it at the top of your editor subtree with the props you need. The full type-level reference lives at WorkflowBuilderRoot under API Reference; this page focuses on what each prop does and when you reach for it.

import { WorkflowBuilder } from '@workflowbuilder/sdk';
<WorkflowBuilder.Root nodeTypes={[/* ... */]} integration={/* ... */} />;

All props optional unless marked.

PropTypeDefaultDescription
integrationWorkflowBuilderIntegration{ strategy: 'localStorage' }How the builder loads and persists diagram data.
nodeTypesPaletteItemOrGroup[][]Node type definitions rendered in the palette and used for validation.
templatesTemplateModel[][]Diagram templates available in the template selector.
jsonFormWorkflowBuilderJsonFormConfigCustom JSONForms renderers, cells, and translations.
pluginsWorkflowBuilderPlugin[]Plugin initializer functions. Each called once on first mount.
namestringWorkflow name displayed in the header.
layoutDirection'DOWN' | 'RIGHT''DOWN'Flow direction of the diagram.
initialNodesWorkflowBuilderNode[][]Initial diagram nodes (used by the props integration strategy).
initialEdgesWorkflowBuilderEdge[][]Initial diagram edges (used by the props integration strategy).
childrenReactNode<DefaultLayout />Custom layout. Omit children for the default floating-overlay layout.

Build your own layout by composing the namespaced subcomponents:

ComponentRenders
WorkflowBuilder.TopBarApp-bar with name, controls, toolbar.
WorkflowBuilder.PalettePalette of node types (draggable).
WorkflowBuilder.Canvasxyflow canvas with nodes, edges, drag-drop.
WorkflowBuilder.PropertiesPanelProperties sidebar driven by JsonForms.
WorkflowBuilder.DefaultLayoutThe default floating-overlay arrangement of the four above.

To extend the default layout (e.g. add a banner alongside):

<WorkflowBuilder.Root nodeTypes={[]}>
<WorkflowBuilder.DefaultLayout />
<MyTopBanner />
</WorkflowBuilder.Root>
type PaletteItemOrGroup = PaletteItem | PaletteGroup;

The SDK ships no default palette — nodeTypes must be supplied for the palette to have content. Each PaletteItem carries its own schema (JSON Schema) and uischema (JSONForms UI Schema) — those schemas drive the property panel rendered by JSONForms.

<WorkflowBuilder.Root
nodeTypes={[
{
type: 'myCustomNode',
label: 'My Custom Node',
schema: {
/* JSON Schema */
},
uischema: {
/* UI Schema */
},
// … (see PaletteItem type for the full shape)
},
]}
integration={{ strategy: 'props', onDataSave }}
/>

WorkflowBuilderIntegration is a discriminated union. Each strategy defines where the builder reads initial state and where it writes on save.

type WorkflowBuilderIntegration =
| { strategy?: 'localStorage' }
| { strategy: 'api'; endpoints: { load: string; save: string } }
| { strategy: 'props'; onDataSave: OnSaveExternal };
StrategyInitial dataSave targetUse when
localStorageBrowser localStorage['workflowBuilderDiagram']Same key in localStoragePrototyping, demos, default behavior
apiGET to endpoints.loadPOST JSON to endpoints.saveBackend-managed persistence
propsinitialNodes / initialEdges instance propsonDataSave callbackHost app manages persistence itself
<WorkflowBuilder.Root />
// integration omitted — localStorage is the default
<WorkflowBuilder.Root
integration={{
strategy: 'api',
endpoints: {
load: '/api/workflow/load',
save: '/api/workflow/save',
},
}}
/>
<WorkflowBuilder.Root
name="wf-1"
initialNodes={
[
/* ... */
]
}
initialEdges={
[
/* ... */
]
}
integration={{
strategy: 'props',
onDataSave: async (data, params) => {
const response = await fetch('/api/workflows', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
// The runtime renders a success snackbar on any non-empty resolution
// (`'success'`, `'error'`, `'alreadyStarted'` all look the same at the
// UI layer). Throw to surface the error snackbar instead.
if (!response.ok) throw new Error(`Save failed: ${response.status}`);
return 'success';
},
}}
/>
type OnSaveExternal = (data: IntegrationDataFormat, savingParams?: OnSaveParams) => Promise<DidSaveStatus>;
type IntegrationDataFormat = {
name: string;
layoutDirection: LayoutDirection;
nodes: WorkflowBuilderNode[];
edges: WorkflowBuilderEdge[];
};
type OnSaveParams = { isAutoSave?: boolean };
type DidSaveStatus = 'success' | 'error' | 'alreadyStarted';

Today the runtime treats every non-empty resolution of onDataSave as “the save finished”, surfacing the success-style snackbar — 'success', 'error', and 'alreadyStarted' all behave the same way at the UI level. Throw from onDataSave instead of resolving to 'error' if you need an error snackbar.

import { WorkflowBuilder } from '@workflowbuilder/sdk';
import '@workflowbuilder/sdk/style.css';
export function App() {
return (
<WorkflowBuilder.Root
name="my-workflow"
initialNodes={[]}
initialEdges={[]}
integration={{
strategy: 'props',
onDataSave: async (data) => {
console.log('save:', data);
return 'success';
},
}}
/>
);
}