astro react flow
published: 6/15/2026
written by: Stefan Johansson
7 min read

React Flow is a mature open-source library for building node-and-edge diagrams — flowcharts, architecture maps, pipelines — with panning, zooming, and dragging built in. It is React, though, and Astro pages are mostly static HTML. @sjohansson/astro-reactflow bridges that gap: it wraps React Flow in a single component you can drop into any Astro page, and an integration that handles the React wiring for you.
Every diagram in this post is live — drag the nodes, zoom with the scroll wheel, open one in fullscreen, and switch the site theme to watch the colors follow. It’s part of the Astro Components collection.
What the wrapper adds
You could wire React Flow into Astro yourself — I did exactly that in an earlier post that builds the component from scratch. The package packages up that work, plus the polish, into one component:
- A title bar and description footer around the canvas.
- Focus mode — a fullscreen view, with
Escto exit. - A minimap with input/output nodes color-coded.
- Automatic light/dark theming that follows the host page.
- PNG / SVG export buttons, when you want them.
- Background variants (dots, lines, cross), arrowheads, per-edge styling.
And, importantly, an Astro integration that registers React and applies the build settings React Flow needs — so you don’t have to.
Setup: the integration does the wiring
Install the package alongside its peers:
pnpm add @sjohansson/astro-reactflow @astrojs/react @xyflow/react react react-dom
Then register the integration once:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import reactFlow from '@sjohansson/astro-reactflow/integration';
export default defineConfig({
integrations: [reactFlow()],
});
That single line does two jobs. First, it auto-registers @astrojs/react if it isn’t already there — so you don’t have to add react() yourself. (If it’s already registered, the integration detects that and skips it.) Second, it applies the Vite SSR settings React Flow needs: it adds @xyflow/react to ssr.noExternal and pre-bundles the CommonJS dependencies React Flow and Zustand pull in, which otherwise trip up the build.
If @astrojs/react is missing and auto-registration is turned off, the
integration throws a clear error at config-load time — instead of letting the
build fail later with React Flow’s cryptic NoMatchingRenderer message.
Then use the component, with one rule:
---
import { ReactFlowWrapper } from '@sjohansson/astro-reactflow';
---
<ReactFlowWrapper
client:only="react"
title="Basic Process Flow"
nodes={[
{ id: '1', type: 'input', label: 'Start', position: { x: 250, y: 0 } },
{ id: '2', label: 'Process Data', position: { x: 250, y: 110 } },
{ id: '3', type: 'output', label: 'Complete', position: { x: 250, y: 220 } },
]}
edges={[
{ id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e2-3', source: '2', target: '3' },
]}
height={400}
/>
client:only="react" is required. React Flow measures the DOM and has no
server-rendered output, so the diagram must render entirely in the browser as
an Astro island. Use
client:only, not client:load — there is nothing to hydrate from.
A first diagram
Here is that pattern rendered for real — the lifecycle of one of these diagrams on an Astro page, with a minimap turned on:
Click the expand icon in the top-right corner to open the diagram in fullscreen
focus mode — handy for larger diagrams. Press Esc to return.
The data model
A diagram is just two arrays: nodes and edges. The wrapper uses a friendly,
flat shape so you rarely touch React Flow’s internals directly.
A node needs an id, a label, and a position.
The type is optional — input and output get distinct styling and minimap colors:
interface DiagramNode {
id: string;
type?: 'default' | 'input' | 'output' | 'group';
label: string; // hoisted to React Flow's data.label for you
position: { x: number; y: number };
}
An edge connects two node ids and can be animated, labelled, or restyled:
interface DiagramEdge {
id: string;
source: string;
target: string;
label?: string;
animated?: boolean;
type?: 'default' | 'straight' | 'step' | 'smoothstep' | 'bezier'; // default: smoothstep
markerEnd?: 'arrow' | 'arrowclosed' | boolean; // overrides the diagram default
strokeWidth?: number; // overrides the diagram default
}
If you read the earlier from-scratch post,
the shape will look familiar — but the API differs. That local component takes
a single definition={{ nodes, edges }} object; the published wrapper takes
nodes and edges as separate top-level props. Same building blocks,
slightly tidier ergonomics.
Branching diagrams, arrows, and backgrounds
Nodes can fan out, edges can carry arrowheads, and the canvas background has three variants.
This one maps how this very page is built, using defaultMarkerEnd for arrows on every edge and the lines background:
The description prop renders in a footer bar under the canvas — useful for a caption that travels with the diagram.
Make it interactive
By default diagrams are static to read: you can pan and zoom, but nodes stay put.
Set interactive and readers can drag nodes, select them, and draw new connections between handles:
Drag from the small handle on the edge of a node to another node to draw a new
connection. Interactive diagrams are great for editors and playgrounds; leave
interactive off for documentation you want readers to study, not rearrange.
Export to PNG or SVG
Set enableExport and the diagram gains PNG and SVG export buttons in the top-left —
so a diagram on the page can become an image in a slide deck or doc:
Theming: it follows the page
By default the wrapper runs colorMode="auto", which reads the host page’s
theme and re-evaluates whenever it changes.
It looks at <html> for a dark/scheme-dark class, a data-theme-scheme="dark" attribute,
or a data-theme value containing "dark", and falls back to the OS prefers-color-scheme.
That means it works out of the box with the @sjohansson/astro-theme-toggle
component this site uses — flip the theme in the footer and every diagram
above recolors with no extra code. Pin it with colorMode="light" or colorMode="dark" if you’d rather it not follow along.
The colors themselves are CSS custom properties on .reactflow-wrapper,
so you can map the diagram onto your own design tokens:
.reactflow-wrapper {
--arf-surface: var(--my-surface-color); /* canvas background */
--arf-chrome-surface: var(--my-chrome-color); /* title / footer / controls */
--arf-border: var(--my-border-color); /* borders and node outlines */
--arf-text: var(--my-text-color); /* text and edge strokes */
}
The resolved mode is also mirrored as data-color-mode="light|dark" on the wrapper, so you can target either state from your own CSS.
Props worth knowing
The full list is in the package README; these are the ones you’ll reach for most:
| Prop | Type | Default | Purpose |
|---|---|---|---|
nodes / edges | arrays | — | The diagram itself. |
title / description | string | — | Header bar / footer caption. |
height / width | number | string | 400 / "100%" | Container size (numbers are px). |
showMiniMap | boolean | false | Show the color-coded minimap. |
backgroundVariant | "dots" | "lines" | "cross" | "dots" | Canvas background pattern. |
interactive | boolean | false | Allow drag / connect / select. |
allowFocusMode | boolean | true | Show the fullscreen expand button. |
enableExport | boolean | false | Show PNG / SVG export buttons. |
colorMode | "auto" | "light" | "dark" | "auto" | Theme tracking. |
defaultMarkerEnd | "arrow" | "arrowclosed" | boolean | false | Arrowheads for all edges. |
React, React Flow, and @astrojs/react are all peer dependencies — you
bring your own versions and the package slots into them, so there’s never a
second copy of React in your bundle.
Wrapping up
@sjohansson/astro-reactflow turns React Flow into something you can use on an Astro page in two steps: one integration line, one client:only component. You get focus mode, a minimap, theming, and export for free, and the diagram stays a client island so the rest of your page is still plain static HTML.
If you’d like to see how the same result is built by hand — the React component, the Astro wrapper, the theming glue — that’s the subject of the from-scratch diagrams post. And for the theme switching these diagrams ride along with, see astro-theme-toggle.