Skip to content

ag-gr-hub/flowchart-sequence-designer

flowchart-sequence-designer

npm version CI CodeQL

A TypeScript-first Bun/npm package for building and editing flowchart and sequence diagrams — both programmatically via a fluent API and visually via a React drag-and-drop canvas editor.

🔗 Live demo & developer docs → Open it to drive the editor, switch variants (Flowchart / Question / Journey / Sequence), and copy the same API snippets shown below straight from the docs tab. Every variant boots with a working sample diagram so you can poke without any setup.

Quick start

import { DiagramEditor } from 'flowchart-sequence-designer/ui';

export default function App() {
  return <DiagramEditor height={520} onChange={(m) => console.log(m)} />;
}

That's it — no provider, no theme setup, no required props. The editor mounts with a sample diagram, a working toolbar, undo/redo, drag-to-pan, scroll-to-zoom, and export buttons for Mermaid / PlantUML / JSON / SVG / PNG. Pass theme="dark" or themeOverrides={…} to brand-match, or initialModel={emptyModel('flowchart')} to start blank.

Install

bun add flowchart-sequence-designer
# or
npm install flowchart-sequence-designer

React 18+ is a peer dependency for the UI components. The core API has zero runtime dependencies.


Programmatic API

Flowchart

import { flowchart } from 'flowchart-sequence-designer';

const diagram = flowchart('Order Flow')
  .node('start',   'Start',           { shape: 'circle' })
  .node('check',   'Payment valid?',  { shape: 'diamond' })
  .node('success', 'Confirm order',   { shape: 'rectangle' })
  .node('fail',    'Reject',          { shape: 'rectangle' })
  .edge('start',   'check')
  .edge('check',   'success', { label: 'Yes' })
  .edge('check',   'fail',    { label: 'No' });

console.log(diagram.toMermaid());

Node shapes

Shape Description
rectangle Standard process box (default)
diamond Decision / branch
circle Start or end terminal
parallelogram Input / output

Edge options

.edge(from, to, {
  label?: string,
  style?: 'solid' | 'dashed' | 'dotted',
  arrowhead?: 'arrow' | 'open' | 'none',
})

Sequence diagram

import { sequence } from 'flowchart-sequence-designer';

const diagram = sequence('Auth Flow')
  .actor('User')
  .actor('Server')
  .message('User',   'Server', 'POST /login')
  .message('Server', 'User',   '200 OK + token', { style: 'dashed' });

console.log(diagram.toMermaid());

Actors auto-register from message() calls, so you can skip .actor() if you prefer.


Export formats

Every builder exposes the same export methods:

diagram.toMermaid()   // string
diagram.toPlantUML()  // string
diagram.toJSON()      // string (serialised DiagramModel)
diagram.toSVG()       // string (SVG markup)
diagram.toPNG()       // Promise<Blob>  (browser only)

Import

import { fromMermaid, fromJSON } from 'flowchart-sequence-designer';

const model = fromMermaid('graph TD; A-->B; B-->C');
const model2 = fromJSON(jsonString);

Round-trip fidelity: fromMermaid(diagram.toMermaid()) produces an equivalent model.


Exporter / importer round-trip rules

The five export formats trade fidelity for portability. Use this table to pick the one that matches what you need:

Format Round-trip Preserved Dropped or lossy
JSON ✅ full every field — variant, metadata, waypoint, x/y positions, edge arrowheads, message order nothing
Mermaid (flowchart) partial node shapes ([] {} (()) [/]), labels, edge connectors (-->, -.->, ---, -.-), edge labels, subgraphmetadata.group positions, waypoint, metadata.answers, variant. Dotted edges collapse to dashed.
Mermaid (sequence) partial actor order, message arrows (->>, -->>), labels message metadata, styling overrides
PlantUML (flowchart) export-only edge styles (--> / -[dashed]-> / -[dotted]->), labels, node id shape distinctions (PlantUML state-diagram syntax is coarser), positions, metadata, variant
PlantUML (sequence) export-only actor order, message style (->, -->), labels
SVG export-only (rendered) full visual parity with the canvas — same dot grid, same edge curves, same node styling
PNG export-only (rendered, browser-only) same as SVG, rasterized at devicePixelRatio

If you need 100% round-trip fidelity, use JSON. If you need a format that GitHub renders inline in markdown, use Mermaid. If you need a polished image for documentation, use SVG or PNG.


Presets

import {
  presetFlowchartModel,
  presetSequenceModel,
  emptyModel,
} from 'flowchart-sequence-designer/ui';

presetFlowchartModel('flowchart')  // 6-node order flow with one decision
presetFlowchartModel('question')   // 1-question / 3-answer router
presetFlowchartModel('journey')    // 5-step onboarding sequence
presetSequenceModel()              // 3-actor login handshake

emptyModel('flowchart')            // { type:'flowchart', variant:'flowchart', nodes:[], edges:[] }
emptyModel('flowchart', 'journey') // same with variant: 'journey'
emptyModel('sequence')             // { type:'sequence', nodes:[], edges:[], actors:[], messages:[] }

All presets return a deep clone — mutate the result freely.


Working with the model directly

import { Model } from 'flowchart-sequence-designer';

const m = new Model('flowchart');         // new Model(type, title?, variant?)
m.addNode({ id: 'a', label: 'Step A', shape: 'rectangle' });
m.addNode({ id: 'b', label: 'Step B', shape: 'rectangle' });
m.addEdge({ id: 'e1', from: 'a', to: 'b', label: 'next' });

// Rehydrate from a saved DiagramModel:
// const m2 = Model.fromData(savedJson);

React UI component

Import from the /ui sub-entry to keep React out of the bundle for non-UI consumers:

import { DiagramEditor } from 'flowchart-sequence-designer/ui';

Basic usage

<DiagramEditor />

Mounted without an initialModel the editor boots with a small working sample diagram for the chosen variant — a 6-node order-flow for flowchart, a role-picker for question, a 5-step onboarding for journey, and a 3-actor login handshake for the SequenceEditor. This gives anyone evaluating the package something to interact with from the first render. To start blank instead:

import { DiagramEditor, emptyModel } from 'flowchart-sequence-designer/ui';

<DiagramEditor initialModel={emptyModel('flowchart')} />

The presets are also exported in case you want to hydrate them from your own code: presetFlowchartModel(variant?) and presetSequenceModel().

All props

<DiagramEditor
  initialModel={model}          // pre-load a DiagramModel
  onChange={(m) => save(m)}     // fires on every node/edge change
  onExport={(fmt, content) => } // intercept exports instead of auto-downloading
  height="100%"                 // any CSS height (default: 600)
  variant="flowchart"           // 'flowchart' | 'question' | 'journey'
  theme="auto"                  // 'light' | 'dark' | 'auto'
  allowedExports={['json','svg']} // restrict visible export buttons
  allowImport={true}            // show/hide the Import button
  themeOverrides={{             // optional per-color overrides
    canvas: '#0b0f1a',
    nodeSelectedFill: '#1f2a44',
  }}
/>

Diagram variants

Variant Description
flowchart General purpose — any shapes, freeform connections
question Each node is a question with lettered answer options (A, B, C…). Each answer has its own connection port.
journey Numbered milestone steps — user path or process walkthrough

Editor features

Canvas

  • Drag nodes to reposition (snaps to 24px grid)
  • Scroll to zoom in/out (pinch to zoom on touch)
  • Drag the canvas background to pan (one-finger pan on touch)
  • Double-click a node to rename it inline
  • Dashed alignment guides appear when a dragged node lines up with a sibling's edge or center, and it snaps within 4 px
  • Bottom-right minimap — click or drag to pan the viewport
  • Accessibility: every node, port, and control is keyboard-reachable with a visible focus ring; selection / add / delete actions announce via an aria-live status region; the edge-flow animation honours prefers-reduced-motion

Connecting nodes

  • Hover a node to reveal the bottom port dot, then drag it to another node
  • Question variant: each answer row has its own port dot — drag it to route that answer to a specific node

Node Navigator (left panel)

  • Lists all nodes with shape badge, label, and connection counts
  • Search/filter by name
  • Click any row to jump to that node and center the canvas on it
  • Collapses to a slim icon strip

Step Editor (right panel)

  • Appears when a node is selected
  • Edit the node name, change its shape
  • Manage branches / answer options (add, remove, reorder)
  • Question variant shows connection status per answer

Context menu (right-click)

  • On canvas: Add node at cursor, Re-center, Undo, Redo
  • On node: Rename, Duplicate, Disconnect all edges, Delete
  • On edge: Style (solid/dashed/dotted), Arrowhead, Reset routing, Delete
  • On touch devices: long-press the canvas (~550ms) opens the canvas menu

Keyboard shortcuts

Shortcut Action
Ctrl+Z Undo
Ctrl+Y / Ctrl+Shift+Z Redo
Ctrl+0 Fit all nodes in view
Ctrl+C / Ctrl+V Copy and paste the current selection (internal edges preserved, +24 px offset on paste)
Ctrl+D Duplicate the current selection
Delete / Backspace Remove the current selection
Escape Deselect, cancel in-flight edge drag, close context menu
Arrow keys Nudge selection by 1 grid unit (Shift = 4 units)
Alt+Arrow Traverse to the nearest node in that direction from the current selection
Shift+click Toggle a node in/out of the current selection
Shift+drag (empty canvas) Box-select — add every intersected node to the selection
Double-click edge label Rename the edge label inline
Drag edge midpoint Route the edge through a waypoint (right-click → Reset routing to clear)

Export / Import

  • Toolbar exports to Mermaid, PlantUML, JSON, SVG, PNG
  • Import accepts Mermaid syntax or JSON

Theming

<DiagramEditor theme="dark" />    // force dark
<DiagramEditor theme="light" />   // force light
<DiagramEditor theme="auto" />    // follows system prefers-color-scheme (default)

To match the editor to a host application's brand, pass themeOverrides — a Partial<ThemeColors> that is shallow-merged on top of the resolved light/dark palette:

import { DiagramEditor, type ThemeColors } from 'flowchart-sequence-designer/ui';

const brand: Partial<ThemeColors> = {
  canvas: '#0b1020',
  nodeFill: '#111a2e',
  nodeStroke: '#2b3a5a',
  nodeSelectedFill: '#1a2447',
  edgeColor: '#7b8aa6',
  textPrimary: '#e6edf7',
};

<DiagramEditor theme="dark" themeOverrides={brand} />;

Every field on ThemeColors (canvas, nodeFill, nodeStroke, edgeColor, panelBg, inputBg, …) is overridable. Sequence diagrams accept the same prop with a slightly different shape — Partial<SequenceThemeColors> — also exported from flowchart-sequence-designer/ui.


SequenceEditor

<DiagramEditor> auto-delegates to <SequenceEditor> when handed a sequence model, but you can also mount it directly to avoid the type-check redirect:

import { SequenceEditor, presetSequenceModel } from 'flowchart-sequence-designer/ui';

<SequenceEditor
  initialModel={presetSequenceModel()}
  height={520}
  theme="dark"
  onChange={(m) => save(m)}
/>

SequenceEditor props (mirrors DiagramEditorProps minus variant):

Prop Type Default Notes
initialModel DiagramModel preset Must have type: 'sequence'. Falls back to the preset if a non-sequence model is passed.
onChange (m: DiagramModel) => void Fires on every committed mutation.
onExport (format, content) => void download Receives string for text formats, Blob for PNG.
height number | string 600 Any CSS height.
allowedExports ExportFormat[] all Whitelist of toolbar export buttons.
allowImport boolean true Show the Import button.
theme 'light' | 'dark' | 'auto' 'auto' auto follows OS prefers-color-scheme.
themeOverrides Partial<SequenceThemeColors> Per-property palette overrides.

Sequence-specific interactions:

  • Drag a message row by its handle to reorder messages.
  • Double-click a message label to rename inline.
  • Drag the column header of an actor to reorder lifelines.

Restricting exports and import

// Only allow JSON and SVG download
<DiagramEditor allowedExports={['json', 'svg']} />

// Hide the import button entirely
<DiagramEditor allowImport={false} />

// Handle exports yourself (e.g. send to an API)
<DiagramEditor
  onExport={(format, content) => {
    if (format === 'json') myApi.save(content as string);
  }}
/>

Framework Wrappers

Framework Package Docs
Angular @flowchart-sequence-designer/angular Docs & Demo
Vue @flowchart-sequence-designer/vue Docs & Demo

Types

Core entry (flowchart-sequence-designer)

import type {
  DiagramModel,
  DiagramNode,
  DiagramEdge,
  DiagramVariant,
  DiagramType,
  NodeShape,
  ExportFormat,
  SequenceMessage,
  ValidationError,
} from 'flowchart-sequence-designer';

import {
  Model,                 // class — build / query / validate diagrams
  flowchart,             // fluent FlowchartBuilder factory
  sequence,              // fluent SequenceBuilder factory
  toMermaid, fromMermaid,
  toPlantUML,
  toJSON, fromJSON,
  toSVG, toPNG,
} from 'flowchart-sequence-designer';

UI entry (flowchart-sequence-designer/ui)

import {
  DiagramEditor,         // flowchart / question / journey editor
  SequenceEditor,        // sequence diagram editor
  Toolbar,               // standalone toolbar (used internally)
  StepEditor,            // node property panel (used internally)
  presetFlowchartModel,  // starter model for flowchart variants
  presetSequenceModel,   // starter model for sequence
  emptyModel,            // blank model factory
} from 'flowchart-sequence-designer/ui';

import type {
  DiagramEditorProps,
  SequenceEditorProps,
  ThemeColors,           // flowchart theme palette
  SequenceThemeColors,   // sequence theme palette
} from 'flowchart-sequence-designer/ui';

DiagramModel

interface DiagramModel {
  type: 'flowchart' | 'sequence';
  variant?: DiagramVariant;    // 'flowchart' | 'question' | 'journey' (flowchart-type only)
  title?: string;
  nodes: DiagramNode[];        // always present (empty array for sequence models)
  edges: DiagramEdge[];        // always present (empty array for sequence models)
  actors?: string[];           // sequence models only — ordered actor names
  messages?: SequenceMessage[]; // sequence models only — ordered messages
}

DiagramNode

interface DiagramNode {
  id: string;
  label: string;
  shape?: 'rectangle' | 'diamond' | 'circle' | 'parallelogram';
  x?: number;
  y?: number;
  metadata?: Record<string, unknown>;
  // question variant: metadata.answers = string[]
}

DiagramEdge

interface DiagramEdge {
  id: string;
  from: string;
  to: string;
  label?: string;
  style?: 'solid' | 'dashed' | 'dotted';
  arrowhead?: 'arrow' | 'none' | 'open';
  waypoint?: { x: number; y: number }; // manual routing point (JSON only)
}

SequenceMessage

interface SequenceMessage {
  id: string;
  from: string;            // actor name
  to: string;              // actor name
  label: string;
  style?: 'solid' | 'dashed';
}

ValidationError

interface ValidationError {
  kind: 'dangling-from' | 'dangling-to' | 'duplicate-node-id' | 'duplicate-edge-id';
  id: string;
  message: string;
}

Package structure

flowchart-sequence-designer/
├── dist/
│   ├── index.js / index.cjs / index.d.ts   ← core (no React)
│   └── ui/
│       └── index.js / index.cjs / index.d.ts ← React UI
└── src/
    ├── core/          # types, Model, FlowchartBuilder, SequenceBuilder
    ├── exporters/     # mermaid, plantuml, json, svg, png
    ├── importers/     # mermaid, json
    └── ui/            # DiagramEditor, SequenceEditor, Toolbar, hooks

The "." export gives you the core API; "./ui" gives you the React components. Consumers that only use the programmatic API never pull in React.


Security

This package takes security seriously:

  • Input sanitization — All user-provided text is sanitized before rendering (HTML tags, javascript:/data:/vbscript: URIs, on* event handlers, and control characters are stripped). See src/core/sanitize.ts.
  • Resource limits — Importers enforce hard caps (500 nodes, 2000 edges, 100 actors, 2000 messages, 2MB input) to prevent resource exhaustion.
  • Prototype pollution defense — JSON importer strips __proto__, constructor, and prototype keys recursively.
  • SVG export — Defence-in-depth: sanitize first, then XML-escape. Safe even if consumed by less-strict parsers.
  • No eval / innerHTML — The codebase never uses dynamic code execution or raw HTML injection.
  • CodeQL — Automated security scanning runs weekly and on every PR.
  • Dependabot — Dependency updates monitored weekly.

To report a vulnerability, see SECURITY.md.


Building from source

bun install
bun run build        # outputs to dist/
bun test             # 105 tests
bun run typecheck    # tsc --noEmit
bun run lint         # eslint
bun run format:check # prettier --check

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors