Documentation
Flora is a fault-tolerant, Mermaid-compatible diagram library that renders beautiful, interactive SVGs. It never throws on bad input — it renders what it can and returns structured warnings for the rest.
Installation
npm install @topspinj/flora Flora ships as ESM and CJS with full TypeScript types. One dependency (dagre for layout).
Quick Start
import { render } from "@topspinj/flora";
const { warnings } = render(
`flowchart LR
A[Start] --> B{Decision}
B -->|Yes| C[Do thing]
B -->|No| D[Other thing]`,
document.getElementById("diagram"),
{ interactive: true }
);
// warnings is an array of { line, col, message }
The render() function parses the input, computes layout, and appends an interactive SVG to the target element.
It returns any parse warnings so you can surface them in your UI.
Playground
The easiest way to experiment with Flora is the hosted playground — a browser-based editor with live preview, theme switching, lineage highlighting, starter examples, and SVG / PNG export. No install required.
Shareable URLs
The playground encodes your diagram source and theme in the URL fragment as you type, so sharing a diagram is just copying the URL (the Share button does this for you). The format is:
https://florajs.dev/playground/#flora:<base64url(deflate(JSON{ code, theme }))>
The fragment is compressed with deflate-raw and never sent to the server, so
diagrams stay private to whoever holds the link.
Embedding
The hosted /embed route renders a diagram as a clean, chrome-free page,
made for <iframe> embeds on platforms that allow HTML but not custom
scripts — Ghost, Substack, Notion, and similar. The playground's
Embed button copies a ready-to-paste iframe snippet for the current
diagram:
<iframe
src="https://florajs.dev/embed#flora:<encoded>"
width="100%" height="300"
style="border:none;" loading="lazy"
title="Flora diagram">
</iframe>
The diagram source lives in the URL fragment, so it never hits the server. Two formats
are accepted: the playground's compressed #flora:… format (above),
or plain URI-encoded Mermaid source for hand-written embeds:
https://florajs.dev/embed#flowchart%20TD%0A%20%20A%20--%3E%20B Query parameters:
-
theme—default,tufte,digital, orsketch. Overrides the theme encoded in a#flora:fragment. -
interactive=1— enables zoom, pan, and lineage highlighting. Off by default so the embed never captures the host page's scroll.
For auto-sizing, the page posts { type: "flora:height", height }
to the parent window whenever the rendered size changes. On pages where you control
scripts, listen and resize the iframe:
window.addEventListener("message", (e) => {
if (e.data?.type === "flora:height")
document.querySelector("iframe.flora").height = e.data.height;
}); On pages where scripts are allowed, you can skip the iframe and render inline with the CDN bundle instead:
<div id="flora-diagram"></div>
<script type="module">
import { render } from "https://esm.sh/@topspinj/flora";
render(`flowchart TD ...`, document.getElementById("flora-diagram"), { theme: "default" });
</script>
To run the playground locally instead, clone the repo, run
npm install && npm run build, then npx serve . and open
/playground.html.
Markdown / Rehype
Flora ships a rehype plugin that transforms fenced code blocks into rendered SVG diagrams at build time. It works with any framework that supports rehype — Astro, Docusaurus, Next.js, and more.
Usage
Write Flora syntax in a fenced code block with the flora or flowchart language tag:
```flora
flowchart LR
A[Source] --> B{Decision}
B -->|Yes| C[Deploy]
B -->|No| D[Fix]
``` The code block is replaced with a rendered SVG at build time — no client-side JavaScript required.
Astro
// astro.config.mjs
import rehypeFlora from "@topspinj/flora/rehype";
export default defineConfig({
markdown: {
rehypePlugins: [rehypeFlora],
},
}); Docusaurus
// docusaurus.config.js
import rehypeFlora from "@topspinj/flora/rehype";
export default {
presets: [
['@docusaurus/preset-classic', {
docs: { rehypePlugins: [rehypeFlora] },
blog: { rehypePlugins: [rehypeFlora] },
}],
],
}; Next.js
// With next-mdx-remote or @next/mdx
import rehypeFlora from "@topspinj/flora/rehype";
const mdxOptions = {
rehypePlugins: [rehypeFlora],
}; Options
Pass options as the second element in the plugin array:
rehypePlugins: [[rehypeFlora, { theme: "tufte" }]] | Option | Type | Default | Description |
|---|---|---|---|
theme | "default" | "tufte" | "digital" | "sketch" | "default" | Theme preset or custom theme object |
className | string | "flora-diagram" | CSS class added to the wrapper <div> |
languages | string[] | ["flora", "flowchart"] | Code block languages to match |
strict | boolean | true | Throw FloraParseError (failing the build) when a diagram has parse errors or an unsupported type. Set to false to render best-effort. |
Strict mode is on by default so a broken diagram fails your build instead of publishing silently wrong output — see Error Handling.
The rendered diagrams are static SVGs. For interactivity (zoom, pan, lineage highlighting),
use the render() API on the client side.
React
Flora ships a first-class React component that wraps the core render() API.
Import it from @topspinj/flora/react.
Usage
import { Flora } from "@topspinj/flora/react";
function App() {
return (
<Flora
source={`flowchart LR
A[Start] --> B{Decision}
B -->|Yes| C[Deploy]
B -->|No| D[Fix]`}
theme="default"
interactive={true}
onNodeClick={(id) => console.log("Clicked:", id)}
/>
);
}
The component re-renders automatically when source, theme, or callback props change.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Mermaid-compatible diagram source string |
theme | ThemePreset | Partial<FloraTheme> | No | Theme preset name or custom theme overrides |
interactive | boolean | No | Enable zoom, pan, hover, click, and lineage highlighting |
onNodeClick | (nodeId: string) => void | No | Called when a node is clicked |
onNodeHover | (nodeId: string | null) => void | No | Called on mouseenter/mouseleave (null on leave) |
onHighlight | (nodeId, upstream[], downstream[]) => void | No | Called when lineage highlighting is applied |
onWarnings | (warnings: ParseWarning[]) => void | No | Called after each render with any parse warnings |
className | string | No | CSS class name for the container div |
style | React.CSSProperties | No | Inline styles for the container div |
Handling Warnings
Use the onWarnings callback to surface parse warnings in your UI:
function DiagramWithWarnings({ source }) {
const [warnings, setWarnings] = useState([]);
return (
<div>
<Flora source={source} onWarnings={setWarnings} />
{warnings.map((w, i) => (
<p key={i}>Line {w.line}: {w.message}</p>
))}
</div>
);
} CDN / Web Component
Flora ships a single-file browser bundle so you can embed diagrams in any HTML page with
zero build step. The bundle exposes the full core API on window.Flora and
registers a <flora-diagram> custom element.
Usage
<script src="https://unpkg.com/@topspinj/flora"></script>
<flora-diagram theme="default">
flowchart TD
A[Dashboard] --> B[API]
B --> C[(Database)]
</flora-diagram>
The element reads its text content as diagram source and renders into a shadow root, so the
source stays in the light DOM. It re-renders automatically when the text content or
attributes change (via a MutationObserver).
Attributes
| Attribute | Values | Description |
|---|---|---|
theme | default | tufte | digital | sketch | Theme preset name. Unknown names fall back to default. |
interactive | "true" | "false" | Zoom, pan, hover, click, and lineage highlighting. On by default, matching render() and the React component; set interactive="false" to disable. |
Events
When a render produces parse warnings (skipped lines, unterminated blocks), the element
dispatches a bubbling, composed flora-warnings event with
detail.warnings set to the same ParseWarning[] the core API
returns — the CDN equivalent of the React component's onWarnings prop:
document.addEventListener("flora-warnings", (e) => {
console.warn(e.detail.warnings); // [{ line, col, message }, ...]
}); window.Flora
Every export from the core package is available on the global, so you can also drive rendering imperatively without the custom element:
Flora.render("flowchart LR\n A --> B", document.getElementById("diagram"));
Flora.toSVGString("flowchart LR\n A --> B", { theme: "tufte" });
The global also exposes the element class (Flora.FloraDiagramElement) and
Flora.registerFloraDiagram(tagName?) to register the component under a custom
tag name. Registration under flora-diagram happens automatically when the
script loads.
Python
The florajs package on PyPI brings Flora to Python workflows — Jupyter
notebooks, dbt/data-lineage visualization, and headless SVG export. It embeds the JS
library, so diagrams look identical everywhere: to_svg() runs the renderer
in an embedded V8 engine (no browser or Node.js required), and notebook display is fully
interactive.
pip install florajs Usage
from florajs import Flowchart
fc = Flowchart("LR", theme="tufte")
fc.node("raw", "Raw events", shape="cylinder")
fc.node("clean", "Cleaned", shape="stadium")
fc.edge("raw", "clean", "dbt run")
fc # displays interactively in Jupyter
fc.to_svg_file("pipeline.svg") # headless SVG export
Raw Flora/Mermaid syntax works too, via Diagram:
from florajs import Diagram
Diagram("""
flowchart TD
a[Start] --> b{Decide}
b -->|yes| c([Done])
""", theme="sketch") API
| Method | Description |
|---|---|
Flowchart(direction, theme=, strict=) | Programmatic builder; every method below chains |
.node(id, label, shape=, href=, tooltip=, target=) | Add a node; shapes match the syntax shapes |
.edge(source, dest, label, style=, arrow=) | Connect nodes (solid | dotted | thick, arrow | open | bidirectional) |
.subgraph(id) | Context manager: nodes defined inside belong to the group; blocks nest |
.link(id, href, tooltip=, target=) | Attach a click-through link to a node |
Diagram(source, theme=, strict=) | A diagram from raw Flora/Mermaid syntax |
.to_svg() / .to_svg_file(path) | Headless SVG export (embedded V8, no browser) |
.to_html() / .to_html_file(path) | Standalone interactive HTML document |
.source / .warnings | Generated Flora syntax / parser diagnostics |
Errors and Warnings
The Python interface follows Flora's fault-tolerance contract:
skipped lines surface as a Python FloraSyntaxWarning, and strict=True
raises FloraParseError instead. Theme presets and override dicts mirror
the JS themes.
Claude Code
Flora ships an official Claude Code plugin —
a skill that teaches Claude to write correct Flora syntax, choose sensible shapes and
layouts, and visualize dbt lineage straight from a manifest.json. Every
diagram comes back with a playground share link, so you see the
rendered result immediately without installing anything.
Install
/plugin marketplace add topspinj/florajs
/plugin install flora@florajs Usage
Just describe what you want — or invoke the skill directly with /flora:
Claude replies with the diagram and a live link you can open immediately:
flowchart TD
login([User logs in]) --> check{Credentials valid?}
check -->|valid| jwt[Issue JWT]
check -->|invalid| err[Show error]
jwt --> dash([Redirect to dashboard]) Open this diagram in the playground →
The skill triggers on anything that reads like a diagram request — "map out the auth flow
in this codebase", "visualize the lineage in my dbt project", "show me everything downstream
of stg_orders" — even without an explicit "diagram" keyword. Claude replies with Flora
syntax, a live playground link, and — if your project uses @topspinj/flora or
the florajs Python package — runnable integration code. The skill follows the
open Agent Skills format, so it also
works with other agents that support it.
Nodes & Shapes
Define nodes with an ID followed by a shape delimiter. If no shape is specified, the node is a plain rectangle labeled with its ID.
| Shape | Syntax | Example | Use case |
|---|---|---|---|
| Rectangle | [label] | A[My Service] | Default / processes |
| Rounded | (label) | B(Cache) | Intermediate steps |
| Diamond | {label} | C{Decision} | Conditionals / routing |
| Circle | ((label)) | D((Start)) | Start / end points |
| Stadium | ([label]) | E([Result]) | Terminals / outputs |
| Cylinder (upright) | [(label)] | F[(Database)] | Databases / storage |
| Queue (horizontal) | [[label]] | G[[Queue]] | Message queues / Kafka |
flowchart LR
A[Service] --> B(Cache)
B --> C{Route?}
C --> D([Result])
C --> E[(Postgres)]
C --> F[[SQS]] Quoted labels
Wrap a label in double quotes to include characters that would otherwise be read as
delimiters, like brackets and parentheses. The quotes are stripped from the rendered
label, matching Mermaid: A["uses [square] brackets"] renders as
uses [square] brackets. Quotes that don't wrap the whole label are kept
literally, and quoted edge labels (-->|"yes"|) work the same way.
Edges
Edges come in three stroke styles, each in directed, open (undirected), and bidirectional form:
| Style | Directed | Open (no arrow) | Bidirectional |
|---|---|---|---|
| Solid | A --> B | A --- B | A <--> B |
| Dotted | A -.-> B | A -.- B | A <-.-> B |
| Thick | A ==> B | A === B | A <==> B |
Open links render with no arrowhead — useful in architecture diagrams where the relationship isn't directional. Bidirectional arrows show two-way communication. Click-to-highlight traverses both kinds in both directions.
Edge Labels
Add labels between pipe characters, or inline between dashes:
flowchart LR
A -->|yes| B
A -->|no| C
A -- maybe --> D
B -- pairs with --- C Chains
Chain multiple nodes in a single line:
A --> B --> C --> D Node Links
Attach a clickable link to a node with Mermaid's click directive. The node is
wrapped in an SVG <a>, so clicking it navigates to the URL — useful
for linking to documentation, dashboards, or source code.
flowchart LR
A[Docs] --> B[API]
click A "https://docs.example.com" "Open docs"
click B "https://api.example.com" _blank | Form | Meaning |
|---|---|
click A "url" | Node navigates to the URL when clicked |
click A "url" "tooltip" | Adds a hover tooltip (<title>) |
click A "url" _blank | Opens in a new tab — also _self, _parent, _top |
Click lines may appear before or after the node they reference. Unsafe URL schemes
(javascript:, data:, vbscript:) are rejected with a
diagnostic. Mermaid's callback form (click A myCallback) is deliberately
ignored — use the onNodeClick option instead,
which still fires alongside link navigation.
Directions
Set the flow direction after the flowchart keyword:
| Value | Meaning |
|---|---|
TB / TD | Top to bottom (default) |
BT | Bottom to top |
LR | Left to right |
RL | Right to left |
Subgraphs
Group nodes into labeled containers. Subgraphs can be nested and are collapsible by default.
flowchart TD
subgraph Backend
API[API Server] --> DB[(Database)]
API --> Cache(Redis)
end
subgraph Frontend
App[React App]
end
App --> API Subgraphs render as dashed containers with a label pill. Nested subgraphs are supported.
ERD Diagrams Beta
Flora supports entity-relationship diagrams using Mermaid-compatible erDiagram syntax. Entities render as table boxes with attribute rows; relationships use crow's foot notation for cardinality.
ERD support is in beta. The syntax and layout are stable, but the API may see minor changes before 1.0.
Basic syntax
erDiagram
CUSTOMER ||--o ORDER : places
ORDER ||--| LINE-ITEM : contains
CUSTOMER {
int id PK
string name
string email FK
}
ORDER {
int orderNumber PK
date placedAt
} Cardinality notation
| Symbol | Meaning |
|---|---|
|| | Exactly one |
|o / o| | Zero or one |
|{ / }| | One or many |
o{ / }o | Zero or many |
Relationship types
-- = identifying (solid line) | .. = non-identifying (dashed line)
Attribute syntax
ENTITY {
type name [PK|FK|UK] ["optional comment"]
} Entities referenced only in relationships are created implicitly with no attributes.
API Reference
render(input, target, options?)
Parse, layout, and render a diagram into a DOM element. Returns parse warnings.
render(
input: string,
target: HTMLElement,
options?: FloraOptions
): { warnings: ParseWarning[]; error?: Error } This is the primary API. It handles the full pipeline and attaches an interactive SVG to the target element. Subgraph collapse/expand is wired up automatically.
render() never leaves the target element empty. If layout or rendering throws
internally, it shows an error-state SVG and returns the caught error instead of
propagating it. (With strict: true, parse errors still throw
FloraParseError before rendering starts.)
import { render } from "@topspinj/flora";
const { warnings } = render(`flowchart LR
A --> B --> C`, document.getElementById("el"));
if (warnings.length) {
console.warn("Parse warnings:", warnings);
} toSVGElement(input, options?)
Parse, layout, and return an SVG element without attaching it to the DOM. Useful for server-side rendering or when you want to control where the SVG goes.
toSVGElement(
input: string,
options?: FloraOptions
): { svg: SVGSVGElement; warnings: ParseWarning[] } const { svg, warnings } = toSVGElement(`flowchart LR
A --> B`);
container.appendChild(svg); toAST(input, options?)
Parse input and return the abstract syntax tree. No layout or rendering is performed.
toAST(
input: string,
options?: { strict?: boolean }
): { ast: DiagramAST; warnings: ParseWarning[] } const { ast } = toAST(`flowchart LR
A[Start] --> B{Decision}`);
console.log(ast.nodes);
// [{ id: "A", label: "Start", shape: "rect" },
// { id: "B", label: "Decision", shape: "diamond" }]
console.log(ast.edges);
// [{ from: "A", to: "B", style: "solid" }] toLayout(input, options?)
Parse and compute node/edge positions. Returns the layout with x/y coordinates for every node and routed edge points.
toLayout(
input: string,
options?: { strict?: boolean }
): { layout: LayoutResult; erdLayout?: ERDLayoutResult; warnings: ParseWarning[] } For flowchart inputs, layout is populated. For ERD inputs, erdLayout is populated instead.
const { layout } = toLayout(`flowchart LR
A --> B --> C`);
// layout.nodes[0] = { id: "A", x: 90, y: 60, width: 100, height: 50, ... }
// layout.edges[0] = { from: "A", to: "B", points: [{x, y}, ...] }
// layout.width, layout.height — bounding box of the full diagram Options
All API functions that accept options use the FloraOptions interface:
| Option | Type | Default | Description |
|---|---|---|---|
theme | "default" | "tufte" | "digital" | "sketch" | Partial<FloraTheme> | "default" | Theme preset name or custom theme overrides |
interactive | boolean | true | Enable zoom, pan, hover, click, and lineage highlighting |
strict | boolean | false | Throw FloraParseError on parse errors or unsupported types instead of rendering best-effort (see Error Handling) |
onNodeClick | (nodeId: string) => void | — | Called when a node is clicked |
onNodeHover | (nodeId: string | null) => void | — | Called on mouseenter/mouseleave (null on leave) |
onHighlight | (nodeId, upstream[], downstream[]) => void | — | Called when lineage highlighting is applied |
Theming
Presets
Flora ships with four built-in themes:
default
Clean, colorful. Shape-coded nodes with subtle gradients.
tufte
Minimal, print-inspired. Cream background with muted strokes.
digital
Dark mode. Neon accents on a dark slate background.
sketch
Hand-drawn. Wobbly strokes and pastel fills with comic lettering.
render(input, el, { theme: "tufte" });
render(input, el, { theme: "digital" });
Unknown preset names fall back to the default theme everywhere a theme name is
accepted — render(), resolveTheme(), the React component, the
rehype plugin, and the <flora-diagram> element.
Custom Themes
Pass a partial theme object to override specific values. Unspecified properties fall back to the default theme.
render(input, el, {
theme: {
background: "#1a1a2e",
nodeColors: {
fill: "#16213e",
fillGradientEnd: "#16213e",
stroke: "#e94560",
text: "#eee",
},
edgeColors: {
stroke: "#e94560",
label: "#aaa",
labelBackground: "#16213e",
},
shadow: true,
},
}); Theme Reference
Full FloraTheme interface:
| Property | Type | Description |
|---|---|---|
background | string | SVG background color |
nodeColors | NodeColorSet | Default node fill, gradient end, stroke, text |
shapeColors | object | Per-shape overrides: diamond, stadium, rounded, cylinder, queue |
edgeColors | object | stroke, label, labelBackground |
fontFamily | string | Font stack for all text |
fontSize | number | Base font size in pixels |
nodeRadius | number | Border radius for rect nodes |
nodePadding | { x, y } | Horizontal and vertical padding inside nodes |
edgeWidth | number | Stroke width for edges |
shadow | boolean | Enable drop shadow on nodes |
subgraphColors | object | fill, stroke, label for subgraph containers |
Each NodeColorSet has: fill, fillGradientEnd, stroke, text.
Types
Flora exports all TypeScript types from its main entry point. Import them as needed:
import type {
FloraOptions, FloraTheme, ThemePreset,
DiagramAST, FlowchartAST, FlowchartNode, FlowchartEdge,
LayoutResult, LayoutNode, LayoutEdge,
ParseWarning, ParseWarningSeverity, ParseResult,
} from "@topspinj/flora"; Core Types
| Type | Description |
|---|---|
FloraOptions | Options passed to render(), toSVGElement(), etc. |
RenderResult | Return type of render() — { warnings, unsupportedType?, error? } |
ParseResult | Return type of toAST() — { ast, warnings } |
ParseWarning | A structured diagnostic: { line, col, message, severity } |
ParseWarningSeverity | "error" | "info" — see Error Handling |
FloraParseError | Error thrown in strict mode, carrying all diagnostics in .warnings |
DiagramType | "flowchart" | "erd" | "unsupported" |
FlowchartDirection | "TB" | "TD" | "BT" | "LR" | "RL" |
NodeShape | "rect" | "rounded" | "diamond" | "circle" | "stadium" | "cylinder" | "queue" |
EdgeArrowType | "arrow" | "open" | "bidirectional" — arrowhead placement on edges |
NodeLink | { url, tooltip?, target? } — a clickable link from a click directive |
ThemePreset | "default" | "tufte" | "digital" | "sketch" |
Parsing Types
These types represent the abstract syntax tree returned by toAST().
DiagramAST
A union type: FlowchartAST | ERDAST | UnsupportedDiagramAST. Narrow via the type discriminant field.
ERDAST
| Property | Type | Description |
|---|---|---|
type | "erd" | Discriminant |
entities | ERDEntity[] | All entities, including implicit ones created from relationships |
relationships | ERDRelationship[] | All relationships with cardinality and identifying flag |
ERDEntity
| Property | Type | Description |
|---|---|---|
id | string | Entity name (e.g. "CUSTOMER") |
attributes | ERDAttribute[] | Ordered list of attributes |
ERDAttribute
| Property | Type | Description |
|---|---|---|
type | string | Data type (e.g. "int", "varchar(255)") |
name | string | Attribute name |
key | "PK" | "FK" | "UK" | undefined | Key type annotation |
comment | string? | Optional inline comment |
ERDRelationship
| Property | Type | Description |
|---|---|---|
from | string | Source entity ID |
to | string | Target entity ID |
label | string | Relationship label (e.g. "places") |
fromCardinality | ERDCardinality | Cardinality at the source entity |
toCardinality | ERDCardinality | Cardinality at the target entity |
identifying | boolean | true = solid line (--); false = dashed (..) |
ERDCardinality
"zero-or-one" | "exactly-one" | "zero-or-many" | "one-or-many" — maps to standard crow's foot notation symbols.
FlowchartAST
| Property | Type | Description |
|---|---|---|
type | "flowchart" | Discriminant |
direction | FlowchartDirection | Flow direction (TB, LR, etc.) |
nodes | FlowchartNode[] | All parsed nodes |
edges | FlowchartEdge[] | All parsed edges |
subgraphs | FlowchartSubgraph[] | All parsed subgraphs |
FlowchartNode
| Property | Type | Description |
|---|---|---|
id | string | Unique node identifier |
label | string | Display text |
shape | NodeShape | Visual shape of the node |
link | NodeLink? | Clickable link from a click directive |
FlowchartEdge
| Property | Type | Description |
|---|---|---|
from | string | Source node ID |
to | string | Target node ID |
label | string? | Optional edge label |
style | "solid" | "dotted" | "thick" | Edge line style |
arrowType | EdgeArrowType? | Arrowhead placement — "arrow" (default), "open", or "bidirectional" |
FlowchartSubgraph
| Property | Type | Description |
|---|---|---|
id | string | Unique subgraph identifier |
label | string | Display label |
nodeIds | string[] | IDs of nodes inside this subgraph |
parentId | string? | Parent subgraph ID (for nesting) |
UnsupportedDiagramAST
| Property | Type | Description |
|---|---|---|
type | "unsupported" | Discriminant |
detectedType | string | The diagram type that was detected but not supported |
Layout Types
These types represent computed positions returned by toLayout().
LayoutResult
| Property | Type | Description |
|---|---|---|
nodes | LayoutNode[] | Positioned nodes |
edges | LayoutEdge[] | Routed edges with point arrays |
subgraphs | LayoutSubgraph[] | Positioned subgraph containers |
width | number | Bounding box width of the full diagram |
height | number | Bounding box height of the full diagram |
LayoutNode
| Property | Type | Description |
|---|---|---|
id | string | Node identifier |
x, y | number | Center position |
width, height | number | Dimensions |
label | string | Display text |
shape | NodeShape | Visual shape |
link | NodeLink? | Clickable link from a click directive |
LayoutEdge
| Property | Type | Description |
|---|---|---|
from | string | Source node ID |
to | string | Target node ID |
label | string? | Optional edge label |
style | "solid" | "dotted" | "thick" | Edge line style |
arrowType | EdgeArrowType? | Arrowhead placement — "arrow" (default), "open", or "bidirectional" |
points | Array<{ x: number; y: number }> | Routed edge waypoints |
LayoutSubgraph
| Property | Type | Description |
|---|---|---|
id | string | Subgraph identifier |
label | string | Display label |
x, y | number | Top-left position |
width, height | number | Dimensions |
nodeCount | number | Number of contained nodes |
parentId | string? | Parent subgraph ID |
ERDLayoutResult
ERD-specific layout returned in toLayout().erdLayout.
| Property | Type | Description |
|---|---|---|
entities | ERDLayoutEntity[] | Positioned entity boxes |
relationships | ERDLayoutRelationship[] | Routed relationship lines with cardinality |
width | number | Bounding box width |
height | number | Bounding box height |
Theming Types
FloraOptions
| Property | Type | Description |
|---|---|---|
theme | ThemePreset | Partial<FloraTheme> | Theme preset name or custom overrides |
interactive | boolean | Enable interactive features |
onNodeClick | (nodeId: string) => void | Node click callback |
onNodeHover | (nodeId: string | null) => void | Node hover callback |
onHighlight | (nodeId, upstream[], downstream[]) => void | Lineage highlight callback |
FloraTheme
| Property | Type | Description |
|---|---|---|
background | string | SVG background color |
nodeColors | NodeColorSet | Default node colors |
shapeColors | object | Per-shape color overrides for diamond, stadium, rounded, cylinder, queue |
edgeColors | { stroke, label, labelBackground } | Edge rendering colors |
fontFamily | string | Font stack for all text |
fontSize | number | Base font size in pixels |
nodeRadius | number | Border radius for rect nodes |
nodePadding | { x, y } | Internal node padding |
edgeWidth | number | Stroke width for edges |
nodeStrokeWidth | number | Stroke width for node borders |
shadow | boolean | Enable drop shadow on nodes |
handDrawn | boolean | Enable hand-drawn sketch style |
subgraphColors | { fill, stroke, label } | Subgraph container colors |
NodeColorSet
| Property | Type | Description |
|---|---|---|
fill | string | Node fill color |
fillGradientEnd | string | Gradient end color (same as fill for flat) |
stroke | string | Border color |
text | string | Text color |
Error Handling & Diagnostics
Flora's contract is never blank, never silently wrong. A line the parser cannot understand is skipped in full and reported as a diagnostic with its line number — it is never reinterpreted as extra nodes. Everything valid still renders.
const { warnings } = render(`flowchart LR
A[Start] --> B{Decision}
B -->
D[End]`, el);
// Renders A, B, D — skips the broken line
// warnings:
// [
// { line: 3, col: 3, message: "Dangling arrow with no target node — line skipped", severity: "error" }
// ]
Every diagnostic has a severity:
"error"— the input could not be understood; the affected line was skipped and the rendered diagram may be missing it."info"— the input was understood and deliberately ignored (see below).
If nothing parses, render() shows an error card listing the first few
diagnostics instead of a blank SVG. This makes Flora ideal for AI-generated diagrams, where
LLMs often produce slightly broken Mermaid syntax — the mistake is always visible, never silent.
Ignored Mermaid directives
Mermaid's styling and behavior directives solve problems Flora handles natively, so they are
recognized and skipped with an info diagnostic rather than misparsed:
classDef, class, style, linkStyle
(use themes instead), click (use
onNodeClick), %%{init}%% directives, and
direction inside subgraphs. Pasting Mermaid that uses them renders the same graph,
styled by your Flora theme.
Unknown diagram types
Known Mermaid types Flora doesn't support yet (sequenceDiagram, gantt, …)
and unrecognized single-word headers render an “unsupported diagram type” notice instead of being
guessed at. A headerless fragment like A --> B is treated as a flowchart with an
info diagnostic suggesting a flowchart TD header.
Strict mode
Pass strict: true to any API function to throw a FloraParseError on
error-severity diagnostics or unsupported types instead of rendering best-effort. Use it wherever
nobody is watching the output — static site builds, CI, server-side rendering.
import { toSVGString, FloraParseError } from "@topspinj/flora";
try {
const { svg } = toSVGString(source, { strict: true });
} catch (e) {
if (e instanceof FloraParseError) console.error(e.message, e.warnings);
}
The rehype plugin is strict by default: a broken diagram fails the build
rather than publishing a wrong one. Pass strict: false to render best-effort.
Interactive surfaces like the playground stay lenient and show
diagnostics alongside the live render.
Interactivity
When interactive: true (the default), Flora adds:
- Zoom & pan — scroll to zoom, drag to pan
- Hover — nodes highlight on mouseover (stroke widens)
- Click handlers — receive callbacks via
onNodeClick - Subgraph toggle — click subgraph labels to collapse/expand
- Lineage highlighting — click a node to highlight its upstream and downstream
render(input, el, {
onNodeClick: (nodeId) => console.log("Clicked:", nodeId),
onNodeHover: (nodeId) => console.log("Hover:", nodeId),
onSubgraphClick: (id) => console.log("Toggled:", id),
}); Set interactive: false to render a static SVG with no event handlers.
Lineage Highlighting
Click any node to highlight its full lineage — all upstream ancestors and downstream descendants. Non-related nodes and edges are dimmed. Click the background or press Escape to clear.
render(input, el, {
onHighlight: (nodeId, upstream, downstream) => {
console.log(`${nodeId}: ${upstream.length} upstream, ${downstream.length} downstream`);
},
});
The onHighlight callback receives the clicked node ID plus arrays of all upstream and downstream
node IDs, so you can sync highlights with external UI (sidebars, details panels, etc).
Feedback
Flora is young and shaped by the people using it. If something rendered wrong, the docs left you guessing, or you're missing a piece of Mermaid syntax, we want to know — all of it goes through GitHub issues:
- Report a bug — a playground share link is the perfect reproduction, since the URL encodes your diagram
- Request a feature — new syntax, API additions, or theme ideas
- General feedback — anything else, including “this confused me”
Pull requests are welcome too — check the open issues for a place to start.