Skip to main content
FlowDrop’s editor is a rich component with many dependencies. This guide covers strategies to minimize bundle impact and ensure smooth performance at scale.

Bundle sizes

Approximate gzip sizes by entry point:
Entry PointApprox. gzip sizeNotes
@flowdrop/flowdrop/core~10 KBTypes and utilities only — no heavy deps
@flowdrop/flowdrop/editor~180 KBIncludes @xyflow/svelte, Svelte runtime
@flowdrop/flowdrop/form~25 KBForm fields without CodeMirror
@flowdrop/flowdrop/form/code~350 KBIncludes CodeMirror and language packs
@flowdrop/flowdrop/form/markdown~300 KBIncludes CodeMirror markdown mode
@flowdrop/flowdrop/playground~200 KBEditor + session management
@flowdrop/flowdrop~400 KBFull bundle (avoid in production)
Use specific entry points rather than @flowdrop/flowdrop to tree-shake unused modules.

Lazy loading the editor

The editor bundle is large. Load it only when the user navigates to the editor page:
// Vanilla JS — dynamic import
async function mountEditor(container) {
  const [{ mountFlowDropApp }, { createEndpointConfig }] = await Promise.all([
    import('@flowdrop/flowdrop/editor'),
    import('@flowdrop/flowdrop/core')
  ]);
  await import('@flowdrop/flowdrop/styles');

  return mountFlowDropApp(container, {
    endpointConfig: createEndpointConfig('/api/flowdrop')
  });
}

React

import React, { Suspense, lazy } from 'react';

const FlowDropEditor = lazy(() => import('./FlowDropEditor'));

export function EditorPage() {
  return (
    <Suspense fallback={<div>Loading editor...</div>}>
      <FlowDropEditor />
    </Suspense>
  );
}
Where FlowDropEditor is a wrapper component that calls mountFlowDropApp in a useEffect.

Deferring CodeMirror

If you use code or markdown fields but not on every page, load them on demand:
async function mountEditorWithCodeFields(container, endpointConfig) {
  // Load form fields that require CodeMirror only when needed
  const [{ mountFlowDropApp }, { createEndpointConfig }] = await Promise.all([
    import('@flowdrop/flowdrop/editor'),
    import('@flowdrop/flowdrop/core')
  ]);

  // This registers code/markdown fields into the registry
  await import('@flowdrop/flowdrop/form/code');
  await import('@flowdrop/flowdrop/form/markdown');

  return mountFlowDropApp(container, { endpointConfig });
}

SSR guard patterns

FlowDrop accesses window, document, and browser APIs — it cannot run on the server. Always guard your mount calls.

SvelteKit

import { browser } from '$app/environment';
import { onMount } from 'svelte';

let app;
onMount(async () => {
  if (!browser) return;
  const { mountFlowDropApp } = await import('@flowdrop/flowdrop/editor');
  app = await mountFlowDropApp(document.getElementById('editor'), { ... });
  return () => app?.destroy();
});

Next.js (App Router)

'use client';
import dynamic from 'next/dynamic';

const FlowDropEditor = dynamic(() => import('../components/FlowDropEditor'), { ssr: false });

Nuxt

<template>
  <ClientOnly>
    <FlowDropEditor />
  </ClientOnly>
</template>

Vite optimizeDeps

If you see Failed to resolve import errors during development, exclude FlowDrop from Vite’s pre-bundling:
// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    exclude: ['@flowdrop/flowdrop', '@xyflow/svelte']
  }
});
See the Installation guide for more setup tips.

Large workflow performance

For workflows with many nodes (50+):

Use batch updates

const { actions } = app.instance.workflow;

// Apply nodes and edges in a single reactive update
actions.batchUpdate({
  nodes: [...existingNodes, nodeA, nodeB],
  edges: [...existingEdges, edge]
});

Use history transactions

Group related changes into a single undo step:
import { getInstance } from '@flowdrop/flowdrop/editor';
const fd = getInstance();

fd.historyBindings.startTransaction(fd.workflow.current, 'Bulk update');
// ... multiple changes
fd.historyBindings.commitTransaction();

Disable auto-save for programmatic updates

When making many changes programmatically, temporarily disable auto-save:
await mountFlowDropApp(container, {
  endpointConfig,
  features: {
    autoSaveDraft: false, // disable localStorage auto-save
    showToasts: false // disable toast notifications
  }
});
See Mount API for the full features options.