---
title: "Mount API"
description: "Complete reference for mountFlowDropApp(), mountWorkflowEditor(), and mountPlayground() — all options, return values, and lifecycle."
source: https://flowdrop.io/docs/editor/reference/mount-api
site: FlowDrop documentation
---
# Mount API
The mount API lets you embed FlowDrop into any HTML container, regardless of framework.
## `mountFlowDropApp()`
Mounts the full FlowDrop application (sidebar, editor, config panel) into a container.
```typescript
import { mountFlowDropApp } from '@flowdrop/flowdrop/editor';
const app = await mountFlowDropApp(container: HTMLElement, options: FlowDropMountOptions);
```
### Options
```typescript
interface FlowDropMountOptions {
// Data
/** Initial workflow to load */
workflow?: Workflow;
/** Available node types (overrides API fetch) */
nodes?: NodeMetadata[];
// API
/** REST API endpoint configuration */
endpointConfig?: EndpointConfig;
/** Port data type configuration (overrides API fetch) */
portConfig?: PortConfig;
/** Category definitions (overrides API fetch) */
categories?: CategoryDefinition[];
/** Authentication provider for API requests */
authProvider?: AuthProvider;
// Event handlers — the grouped lifecycle events bag
eventHandlers?: FlowDropEventHandlers;
// Features
features?: FlowDropFeatures;
// UI options
/** Show the top navbar @default true */
showNavbar?: boolean;
/** Disable the node sidebar */
disableSidebar?: boolean;
/** Editor interaction mode. @default 'edit' */
mode?: 'edit' | 'readonly' | 'locked';
/** Editor height (CSS value or number) */
height?: string | number;
/** Editor width (CSS value or number) */
width?: string | number;
// Navbar customization
/** Custom navbar title */
navbarTitle?: string;
/** Custom navbar action buttons */
navbarActions?: NavbarAction[];
/** Show settings gear icon in navbar */
showSettings?: boolean;
/** Show the "Connected" status indicator in the navbar @default true */
showStatus?: boolean;
// Pipeline mode
/** Pipeline ID for execution status display */
pipelineId?: string;
// Theme
/** Visual theme — named built-in ('default' | 'minimal') or a custom theme object */
theme?: FlowDropTheme | FlowDropThemeName;
// Instance
/** Identifier for this instance. @default 'default' (auto-generated for extra mounts) */
instanceId?: string;
// Settings
/** Initial settings overrides (theme, behavior, editor, ui, api) */
settings?: PartialSettings;
/** Custom localStorage key for draft auto-save */
draftStorageKey?: string;
/** Where workflow drafts are persisted: 'local' (default), 'session', or a custom adapter */
draftStorage?: DraftStorageOption;
/** Which settings tabs to show in the modal (defaults to all) */
settingsCategories?: SettingsCategory[];
/** Show the "Sync to Cloud" button in the settings modal */
showSettingsSyncButton?: boolean;
/** Show the reset buttons in the settings modal */
showSettingsResetButton?: boolean;
// Format adapters
/** Custom workflow format adapters */
formatAdapters?: WorkflowFormatAdapter[];
/** Format ids this host supports — scopes the Workflow Format picker */
workflowFormats?: string[];
}
```
The `mode` option controls canvas interaction: `'edit'` (the default) allows editing; `'readonly'` and `'locked'` disable all canvas interaction.
Use `workflowFormats` to scope the **Workflow Format** picker in the workflow settings panel to the formats your host can actually handle:
```typescript
await mountFlowDropApp(container, {
workflowFormats: ['flowdrop']
});
```
When one or zero ids remain after filtering, the field is hidden instead of shown as a single-choice select, and the workflow keeps its stored format. Omit the option to offer every registered format.
When you omit `instanceId`, the first mount on the page becomes the default
instance (id `default`); additional mounts get auto-generated ids (`fd-1`,
`fd-2`, …). Every instance scopes its draft and panel storage keys by id
(`flowdrop:draft::…`).
Pass an explicit `instanceId` whenever you mount more than one editor with
drafts enabled, so the keys stay stable across page loads.
### Feature flags (`FlowDropFeatures`)
```typescript
interface FlowDropFeatures {
/** Auto-save the current workflow to localStorage as a draft. @default true */
autoSaveDraft?: boolean;
/** How often to auto-save the draft, in milliseconds. @default 30000 */
autoSaveDraftInterval?: number;
/** Show toast notifications for save success, failure, and API errors.
* Disable if you want to handle notifications yourself via event handlers. @default true */
showToasts?: boolean;
}
```
See [Auto-Save & Drafts](/tutorials/recipes/auto-save-and-drafts) for practical examples.
See [Core Types](/editor/reference/types) for `FlowDropEventHandlers` and `FlowDropFeatures`.
### Return value
```typescript
interface MountedFlowDropApp {
/** This mount's state container — drive the editor programmatically. */
instance: FlowDropInstance;
/** Destroy the editor and clean up resources */
destroy(): void;
/** Check if there are unsaved changes */
isDirty(): boolean;
/** Mark the workflow as saved (clears dirty state) */
markAsSaved(): void;
/** Get the current workflow data */
getWorkflow(): Workflow | null;
/** Trigger save operation */
save(): Promise;
/** Trigger export (downloads JSON file) */
export(): void;
/** Clear all workflow drafts from draft storage. Returns the number of entries removed. */
clearAllDrafts(): number;
}
```
`instance` is the state container — workflow, history, playground, interrupts,
categories, and the rest. Call into it to drive the editor programmatically,
e.g. `app.instance.workflow.addNode(...)` or `app.instance.history.undo()`.
**Call `clearAllDrafts()` on logout.**
Otherwise drafts persist across user sessions on shared devices. It clears the
configured draft storage (`localStorage` unless changed via the `draftStorage`
option).
## `mountWorkflowEditor()`
Mounts just the editor canvas — no navbar, no sidebar. Useful for embedding a minimal editor.
```typescript
import { mountWorkflowEditor } from '@flowdrop/flowdrop/editor';
const editor = await mountWorkflowEditor(container, {
workflow: myWorkflow,
endpointConfig: createEndpointConfig('/api/flowdrop'),
portConfig: myPortConfig, // optional, overrides API
categories: myCategories, // optional, overrides API
instanceId: 'editor-b' // optional — scope to a named instance for multi-editor pages
});
```
Returns the same `MountedFlowDropApp` interface as `mountFlowDropApp()`. `instanceId` follows the same default-instance semantics as `mountFlowDropApp()` above.
## `mountPlayground()`
Mounts the interactive playground for workflow testing.
```typescript
import { mountPlayground } from '@flowdrop/flowdrop/playground';
const playground = await mountPlayground(container, {
workflowId: 'my-workflow-id',
endpointConfig: createEndpointConfig('/api/flowdrop'),
mode: 'standalone', // 'standalone' | 'embedded' | 'modal'
config: {
pollingInterval: 1500,
shouldStopPolling: (status) => ['completed', 'failed'].includes(status),
isTerminalStatus: (status) => ['completed', 'failed'].includes(status)
},
onSessionStatusChange: (newStatus, previousStatus) => {
console.log(`${previousStatus} -> ${newStatus}`);
},
onClose: () => console.log('Playground closed'), // required for embedded/modal
height: '600px',
width: '100%',
initialSessionId: 'resume-session-id', // optional
instanceId: 'playground-b' // optional — scope to a named instance
});
```
`instanceId` follows the same default-instance semantics as `mountFlowDropApp()`.
**Live polling is page-global.**
Playground state (sessions, messages, interrupts) is isolated per instance, but
only one playground can actively poll at a time. Drive a non-polling playground
via `pushMessages()` instead.
### Playground return value
```typescript
interface MountedPlayground {
/** Destroy and clean up */
destroy(): void;
/** Get the current session */
getCurrentSession(): PlaygroundSession | null;
/** Get all sessions */
getSessions(): PlaygroundSession[];
/** Get message count in current session */
getMessageCount(): number;
/** Check if currently executing */
isExecuting(): boolean;
/** Stop polling for messages */
stopPolling(): void;
/** Restart polling */
startPolling(): void;
/** Push poll response data */
pushMessages(response: PlaygroundMessagesApiResponse): void;
/** Reset playground state */
reset(): void;
}
```
## `unmountFlowDropApp()` / `unmountPlayground()`
Clean up a mounted instance. Equivalent to calling `.destroy()` on the returned object.
```typescript
import { unmountFlowDropApp } from '@flowdrop/flowdrop/editor';
unmountFlowDropApp(container);
```
## Lifecycle
1. **Mount** — Call `mountFlowDropApp()` with a container element
2. **Interact** — Use the returned API to control the editor programmatically
3. **Destroy** — Call `.destroy()` or the unmount function to clean up
Registration of custom nodes and fields can happen **before or after** mounting. The node and field registries are instance-scoped — resolve them via `getInstance()` inside the component tree, or the mount handle's `.instance` outside it.
Each registry tracks a version counter that invalidates dependent reactive
reads. When you register a node or field after mount, that counter bumps and
the editor re-resolves, so late registrations take effect.