> ## Documentation Index
> Fetch the complete documentation index at: https://flowdrop.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multiple instances

> Run several isolated FlowDrop editors or playgrounds on a single page.

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 theme={null}
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 theme={null}
<script lang="ts">
  import { App, createFlowDropInstance } from '@flowdrop/flowdrop/editor';

  const left = createFlowDropInstance({ id: 'left' });
  const right = createFlowDropInstance({ id: 'right' });
</script>

<App instance={left} workflow={workflowA} nodes={nodeTypes} />
<App instance={right} workflow={workflowB} nodes={nodeTypes} />
```

`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:<workflowId>`,
`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:<workflowId>` | `flowdrop:draft:left:<workflowId>` |
| 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 theme={null}
<script lang="ts">
  import { getInstance } from '@flowdrop/flowdrop/editor';

  const fd = getInstance();
  // Reactive getters — read them inside $derived or templates:
  const name = $derived(fd.workflow.name);
  const canUndo = $derived(fd.historyBindings.canUndo);
</script>
```

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.

<Accordion title="Why SSR needs a per-render instance">
  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.
</Accordion>

## 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.
