Skip to content

Commit 6618d3f

Browse files
docs(sdk): split quick start from the editor config reference (#36)
1 parent 5dddbff commit 6618d3f

4 files changed

Lines changed: 111 additions & 268 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ The SDK is the only npm-published workspace; everything else under `apps/` and `
145145
```
146146
4. Open PR to `main`. The changeset file is part of the PR diff — reviewer sees the declared bump alongside the change.
147147

148+
**`<WorkflowBuilder.Root>` props live on three surfaces.** The type in `packages/sdk/src/workflow-builder-root/workflow-builder-root.types.ts` is the source of truth; the `/api/core/workflowbuilderrootprops/` reference is generated from its JSDoc and never drifts. Two hand-written tables mirror it: `packages/sdk/README.md` (npm landing) and `apps/docs/src/content/docs/guides/configuring-the-editor.md` (docs guide). When you add, rename, or remove a prop, update both tables in the same change. Descriptions may differ per surface (the README leans on gotchas, the guide on how / when); the set of prop names must match.
149+
148150
**Release moment** (maintainer, not Claude):
149151

150152
1. Open PR `release/vX.Y.Z``release`. In the branch, run `pnpm changeset version` — bumps `packages/sdk/package.json`, regenerates `packages/sdk/CHANGELOG.md`, deletes consumed `.changeset/*.md`.

apps/docs/src/content/docs/get-started/quick-start/wb-as-react-component.mdx

Lines changed: 13 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,9 @@ import { TabItem, Tabs } from '@astrojs/starlight/components';
1515

1616
## Known limitations
1717

18-
- **One instance per page (required).** Multi-instance is not supported.
19-
Plugin / JsonForms / i18n registries are module-level singletons shared
20-
across mounts, and the imperative `useStore.{getState,setState,subscribe}`
21-
facade resolves through a module-level "current" pointer — two
22-
`<WorkflowBuilder.Root>` on the same page would silently fight over both.
23-
If you need multiple "workflows" on one page, render them sequentially
24-
(mount → save → unmount → mount next).
18+
:::caution
19+
Mount only one `<WorkflowBuilder.Root>` per page. Multi-instance is not supported. See [Side effects & limitations](/get-started/side-effects/) for the details and how to swap workflows on one page.
20+
:::
2521

2622
## Installation
2723

@@ -57,36 +53,14 @@ xyflow, JsonForms, i18next, immer and zustand are kept external because
5753
they expose singletons (store identity, i18next instance, frozen-object
5854
caches) — your app and the SDK must share a single copy of each.
5955

60-
### Installing from a local checkout (contributors)
61-
62-
If you're developing against an unpublished build of the SDK, run
63-
`pnpm build:lib` from the monorepo root to produce
64-
`packages/sdk/dist/`, then install the local path in your consumer:
65-
66-
```bash
67-
npm install /path/to/workflow-builder/packages/sdk react react-dom @xyflow/react
68-
```
69-
70-
Local-path installs can resolve React from the library's own
71-
`node_modules`, breaking hook calls. Deduplicate it in your bundler — for
72-
Vite:
73-
74-
```ts
75-
// vite.config.ts
76-
export default defineConfig({
77-
resolve: { dedupe: ['react', 'react-dom', '@xyflow/react'] },
78-
});
79-
```
80-
81-
Published-to-npm installs don't need this.
82-
8356
## Usage
8457

8558
The SDK exposes a single compound component, `WorkflowBuilder`. Mount
8659
`<WorkflowBuilder.Root>` at the top of your editor subtree; with no
8760
children it renders the default layout (top bar, palette, canvas,
88-
properties panel). Compose with the named subcomponents when you need a
89-
custom layout.
61+
properties panel).
62+
63+
`<WorkflowBuilder.Root>` takes a small set of optional props. See [Configuring the editor](/guides/configuring-the-editor/) for the full props reference and for composing a custom layout, or the auto-generated [API reference](/api/core/workflowbuilderroot/).
9064

9165
### Hello world
9266

@@ -117,202 +91,17 @@ function App() {
11791
}
11892
```
11993

120-
### Custom layout
121-
122-
Pass children to skip the default layout and compose your own:
123-
124-
```tsx
125-
<WorkflowBuilder.Root nodeTypes={myNodeTypes}>
126-
<header>
127-
<WorkflowBuilder.TopBar />
128-
</header>
129-
<aside>
130-
<WorkflowBuilder.Palette />
131-
</aside>
132-
<main>
133-
<WorkflowBuilder.Canvas />
134-
</main>
135-
<aside>
136-
<WorkflowBuilder.PropertiesPanel />
137-
</aside>
138-
</WorkflowBuilder.Root>
139-
```
140-
141-
To add custom overlays alongside the default layout, mount it explicitly:
142-
143-
```tsx
144-
<WorkflowBuilder.Root nodeTypes={myNodeTypes}>
145-
<WorkflowBuilder.DefaultLayout />
146-
<MyToastBanner />
147-
</WorkflowBuilder.Root>
148-
```
149-
150-
### Custom layout without the app bar
151-
152-
`<WorkflowBuilder.TopBar />` ships the save, import / export, settings, read-only, and theme controls. When you omit it from a custom layout, reach the same commands through the `useWorkflowBuilderActions()` hook. Call it from any descendant of `<WorkflowBuilder.Root>` and wire the returned callbacks to your own buttons:
153-
154-
```tsx
155-
import { useWorkflowBuilderActions } from '@workflowbuilder/sdk';
156-
157-
function MyToolbar() {
158-
const actions = useWorkflowBuilderActions();
159-
160-
return (
161-
<header>
162-
<button onClick={actions.save}>Save</button>
163-
<button onClick={actions.openImport}>Import</button>
164-
<button onClick={actions.openExport}>Export</button>
165-
<button onClick={actions.openSettings}>Settings</button>
166-
<button onClick={actions.toggleReadOnly}>Read-only</button>
167-
<button onClick={actions.toggleDarkMode}>Theme</button>
168-
</header>
169-
);
170-
}
171-
```
172-
173-
The hook returns a stable object, so you can pass any callback straight to an event handler. See [`WorkflowBuilderActions`](/api/hooks/workflowbuilderactions/) for the full action list. A few notes:
174-
175-
- It must be called from a descendant of `<WorkflowBuilder.Root>`. `save` reads the active [integration strategy](#integration-strategies) via context, so calling the hook outside Root resolves `save()` to `'error'` and logs a warning.
176-
- The hook also exposes layout-direction control the bar does not surface: `setLayoutDirection('RIGHT' | 'DOWN')` (idempotent) and `toggleLayoutDirection({ flipPositions?, fitView? })`. `flipPositions` mirrors each node's `x`/`y` as a naive axis swap. It is not auto-layout and ignores node sizes, so pair it with `fitView`. That is why it lives only on the toggle, not on `setLayoutDirection`.
177-
- The top bar also shows and edits the document name. Render your own with `useStore`: read `s.documentName` and write through `s.setDocumentName`.
178-
179-
## Integration strategies
180-
181-
| Strategy | Source / sink | When to use |
182-
| -------------- | ----------------------------------------------------------- | ---------------------------- |
183-
| `localStorage` | Browser `localStorage` | Prototyping, quick starts |
184-
| `api` | `GET`/`POST` to user-provided endpoints | Backend-managed persistence |
185-
| `props` | `onDataSave` callback + `initialNodes`/`initialEdges` props | Host-app-managed persistence |
186-
187-
```tsx
188-
<WorkflowBuilder.Root integration={{ strategy: 'api', endpoints: { load: '/api/load', save: '/api/save' } }} />
189-
```
190-
191-
## Props reference
192-
193-
| Prop | Type | Description |
194-
| ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
195-
| `nodeTypes` | `PaletteItemOrGroup[]` | Node type definitions. Appear in the palette and drive validation. |
196-
| `nodeTemplates` | `WorkflowBuilderNodeTemplates` | Per-node-type custom renderers. Map of `data.type` → React component, overriding the default node renderer for that type. |
197-
| `diagramTemplates` | `TemplateModel[]` | Diagram templates available in the template selector. |
198-
| `plugins` | `WorkflowBuilderPlugin[]` | Functions registering decorators. Synchronous, executed once. |
199-
| `jsonForm` | `WorkflowBuilderJsonFormConfig` | Custom JsonForms renderers, cells, translations. |
200-
| `integration` | `WorkflowBuilderIntegration` | Data source / sink. Defaults to `localStorage`. |
201-
| `name` | `string` | Workflow name shown in the header. |
202-
| `layoutDirection` | `'DOWN' \| 'RIGHT'` | Initial flow direction. |
203-
| `initialNodes` | `WorkflowBuilderNode[]` | Starting diagram nodes. |
204-
| `initialEdges` | `WorkflowBuilderEdge[]` | Starting diagram edges. |
205-
206-
## Extending JsonForms
207-
208-
Node properties are rendered with [JsonForms](https://jsonforms.io).
209-
Register custom renderers and cells via the `jsonForm` prop. Custom
210-
renderers are tried **before** built-ins so your tester can override
211-
matches on a tie.
212-
213-
```tsx
214-
import { rankWith, uiTypeIs } from '@jsonforms/core';
215-
import { withJsonFormsControlProps } from '@jsonforms/react';
216-
import { WorkflowBuilder } from '@workflowbuilder/sdk';
217-
218-
import '@workflowbuilder/sdk/style.css';
219-
220-
function ColorPicker({
221-
data,
222-
handleChange,
223-
path,
224-
}: {
225-
data: string;
226-
handleChange: (path: string, value: string) => void;
227-
path: string;
228-
}) {
229-
return <input type="color" value={data ?? '#000000'} onChange={(e) => handleChange(path, e.target.value)} />;
230-
}
231-
232-
function App() {
233-
return (
234-
<WorkflowBuilder.Root
235-
jsonForm={{
236-
renderers: [
237-
{
238-
tester: rankWith(5, uiTypeIs('ColorPicker')),
239-
renderer: withJsonFormsControlProps(ColorPicker),
240-
},
241-
],
242-
}}
243-
/>
244-
);
245-
}
246-
```
247-
248-
## Writing a plugin
249-
250-
Plugins are functions that register decorators. Pass them to the `plugins` prop:
251-
252-
```tsx
253-
import { WorkflowBuilder, type WorkflowBuilderPlugin, registerComponentDecorator } from '@workflowbuilder/sdk';
254-
255-
import { MyCustomButton } from './my-custom-button';
256-
257-
const myPlugin: WorkflowBuilderPlugin = () => {
258-
registerComponentDecorator('OptionalAppBarControls', {
259-
content: MyCustomButton,
260-
name: 'MyPlugin',
261-
});
262-
};
263-
264-
function App() {
265-
return <WorkflowBuilder.Root plugins={[myPlugin]} />;
266-
}
267-
```
268-
269-
Plugins are synchronous. If a plugin needs async work (config fetch,
270-
WASM load, feature flag lookup), the consumer awaits it outside the
271-
SDK and constructs the plugin around the resolved value before passing
272-
it to `<WorkflowBuilder.Root>`.
273-
274-
Available slots: `OptionalAppBarControls`, `OptionalAppBarTools`,
275-
`OptionalAppChildren`, `OptionalEdgeProperties`, `OptionalFooterContent`,
276-
`OptionalHooks` (invisible provider slot), `OptionalNodeContent`
277-
(receives `nodeId` prop).
278-
279-
Decorator options:
280-
281-
```ts
282-
registerComponentDecorator('SlotName', {
283-
content: MyComponent, // React component to render
284-
place: 'before', // 'before' | 'after' | 'wrapper'
285-
modifyProps: (p) => p, // modify the host component's props
286-
priority: 0, // higher = rendered first
287-
name: 'UniquePluginName', // prevents duplicate registration
288-
});
289-
```
290-
291-
You can also intercept functions and register translations:
292-
293-
```ts
294-
import { registerFunctionDecorator, registerPluginTranslation } from '@workflowbuilder/sdk';
295-
296-
registerFunctionDecorator('trackFutureChange', {
297-
place: 'before',
298-
callback: ({ params }) => {
299-
/* inspect */
300-
},
301-
});
302-
303-
registerPluginTranslation({
304-
en: { translation: { plugins: { myPlugin: { label: 'My Plugin' } } } },
305-
});
306-
```
307-
30894
## TypeScript
30995

31096
All public types are exported from `@workflowbuilder/sdk`. The full
31197
[API Reference](/api/) is generated by TypeDoc directly from the SDK
31298
source on every docs build, so it never drifts.
31399

314-
## See also
100+
## Next steps
315101

316-
- [via callback persistence](/get-started/persistence/callback/) - pass diagram data and save callbacks as React props
317-
- [Add Custom Node Type](/guides/add-a-custom-node/) - register a new node type with custom properties
318-
- [Design system and customization](/overview/features/design-system-and-customization/) - tokens, themes, and UI customization
102+
- [Configuring the editor](/guides/configuring-the-editor/) - props reference, custom layouts, integration strategies, connection validation
103+
- Persistence: [localStorage](/get-started/persistence/localstorage/), [REST API](/get-started/persistence/rest-api/), [via callback](/get-started/persistence/callback/) - load and save diagram data
104+
- [Add a custom node type](/guides/add-a-custom-node/) - register a new node with its own properties
105+
- [Build a plugin](/guides/build-a-plugin/) - toolbar buttons, decorators, function hooks, translations
106+
- [Custom JsonForms control](/guides/custom-jsonforms-control/) - render node properties with your own components
107+
- [Theming](/get-started/theming/) - design tokens, light / dark, customization

0 commit comments

Comments
 (0)