--- title: "Multiple instances" description: "Run several isolated FlowDrop editors or playgrounds on a single page." source: https://flowdrop.io/docs/editor/multiple-instances site: FlowDrop documentation --- # Multiple instances FlowDrop supports multiple editor instances on one page. Each mount gets its own **state container** — workflow data, undo/redo history, playground sessions, interrupts, and panel state are fully isolated between instances. Editing, deleting, or undoing in one editor never affects another, and destroying one leaves its siblings working. ## Quick start: two editors via the mount API ```typescript import { mountFlowDropApp } from '@flowdrop/flowdrop/editor'; const left = await mountFlowDropApp(document.getElementById('editor-left'), { workflow: workflowA, nodes: nodeTypes, instanceId: 'left' // scopes draft/panel storage keys }); const right = await mountFlowDropApp(document.getElementById('editor-right'), { workflow: workflowB, nodes: nodeTypes, instanceId: 'right' }); // Each handle controls only its own editor: left.isDirty(); // false right.getWorkflow(); // workflowB left.destroy(); // `right` keeps working ``` ## Quick start: two editors in Svelte Create an instance per editor with `createFlowDropInstance()` and pass it via the `instance` prop: ```svelte ``` `WorkflowEditor`, `Playground`, `PlaygroundStudio`, `PlaygroundModal`, and `PlaygroundApp` accept the same `instance` prop. ## The default instance You only need `instanceId` when mounting **more than one** editor. The first mount without an `instanceId` becomes the **page-default instance**, which is what `getInstance()` resolves to for single-editor embeds with no explicit provider. Instances are the API: there are no module-level store singletons. Resolve the owning instance with `getInstance()` inside the component tree, or hold the mount handle's `.instance` outside it. Each instance keeps its own scoped localStorage keys (`flowdrop:draft:default:`, `fd-pipeline-panel-open:default` for the default instance). Additional mounts without an explicit id get auto-generated ones (`fd-1`, `fd-2`, …). Prefer explicit ids whenever drafts are enabled, so each editor's drafts land under a stable, predictable key. ## Instance-scoped storage keys | State | Default instance | Instance with `instanceId: 'left'` | | ------------------- | ------------------------------------- | ---------------------------------- | | Workflow drafts | `flowdrop:draft:default:` | `flowdrop:draft:left:` | | Pipeline panel open | `fd-pipeline-panel-open:default` | `fd-pipeline-panel-open:left` | | Pipeline view mode | `fd-pipeline-view-mode:default` | `fd-pipeline-view-mode:left` | `clearAllDrafts()` sweeps everything under `flowdrop:draft:` — instance sub-namespaces included — so a logout handler still clears all editors at once. ## Accessing instance state programmatically Inside FlowDrop's component tree, resolve the current instance with `getInstance()` (during component init): ```svelte ``` Outside the tree, hold on to the container you created (or the mount handle). The `FlowDropInstance` exposes `workflow`, `history`, `historyBindings`, `playground`, `interrupts`, `categories`, `portCoordinates`, `pipelinePanel`, and `destroy()`. ## What stays page-global (by design) Some state is deliberately shared across all instances on a page: - **Theme and settings** — one `data-theme` and one settings store per page. This includes UI toggles like console-open, sidebar-collapsed, and the bottom-panel tab: toggling them in one editor affects all editors. - **Port-compatibility config** — each instance owns a `PortCompatibilityChecker` at `fd.portCompatibility`, re-initialized by mount from the backend's port config; the last mount's fetched config wins for shared backends. - **API endpoint config** — each instance owns an `ApiContext` at `fd.api`, configured by mount via `fd.api.configure(config, authProvider)`. - **Playground live polling** — playground session/message _state_ is isolated per instance, but the polling timer is page-global: only one playground actively polls at a time. If two playgrounds need concurrent live updates, push responses yourself via the mount handle's `pushMessages()` with your own transport (WebSocket/SSE). ## SSR (SvelteKit) Server rendering works without any extra setup. During SSR, the provider components (`App`, `WorkflowEditor`, …) create a fresh per-render instance automatically. The page-default instance is **browser-only** — module-level mutable state on the server would leak between requests. So FlowDrop gives each render its own instance. Resolving the default instance (e.g. via `getInstance()`) during SSR outside a FlowDrop component tree throws with an explanatory error instead of leaking state. ## Troubleshooting **Two editors share state** — two un-keyed mounts both claimed the default instance. Pass explicit `instanceId`s and resolve state via `getInstance()` / the mount handle's `.instance`. **Drafts collide between editors** — both mounts omitted `instanceId`, so the second got an auto-generated id that changes across reloads. Pass stable explicit ids.