High-performance native flow/node graph rendering and interaction engine for
React Native (Expo Modules). Declarative <FlowCanvas /> component with native
Sugiyama layout, viewport virtualization, gestures, toolbars, and smart edge
routing — all rendered with platform-native APIs (Android Canvas / iOS Core
Graphics), off the JS thread.
- Declarative graph canvas — nodes, edges, ports, groups via plain data
- Native Sugiyama layout (via
expo-sugiyama) — hierarchical TB/BT/LR/RL, off the JS thread - Virtualization — viewport culling + LOD for large graphs
- Interactions — pan/zoom, node drag (rigid groups), lasso/selection, port connections
- Smart edge routing — bezier edges blocked by nodes auto-detour as orthogonal smoothsteps (
config.edges.smart, on by default) - Toolbars & affordances — node/edge/group toolbars,
+add-node suggestion stubs - Custom nodes — render any React component inside a native node frame
- Web support — JS fallback implementation for web
npm install expo-flow expo-sugiyamaexpo-sugiyama (the native layout engine) is a required runtime dependency and
is installed automatically. Both packages are Expo autolinking modules — rebuild
the native app after installing (npx expo prebuild, expo run:ios /
expo run:android). No config plugin or manual linking needed.
import React from "react";
import { FlowCanvas } from "expo-flow";
const graph = {
nodes: [
{
id: "webhook",
type: "webhook",
style: { label: "Webhook", shape: "rect" },
ports: [{ id: "webhook-out", type: "output" }],
},
{
id: "ai",
type: "ai",
style: { label: "AI", shape: "rect" },
ports: [{ id: "ai-in", type: "input" }],
},
],
edges: [
{
id: "e1",
sourceNodeId: "webhook",
sourcePortId: "webhook-out",
targetNodeId: "ai",
targetPortId: "ai-in",
},
],
};
export default function App() {
return (
<FlowCanvas
style={{ flex: 1 }}
graph={graph}
config={{
edges: { type: "bezier", smart: true },
}}
layout={{
token: 1,
type: "hierarchical",
options: { rankdir: "LR" },
}}
onNodeTap={(nodeId) => console.log("tapped", nodeId)}
/>
);
}bezier— cubic curves, bend axis derived from port sides (stable while dragging)smoothstep— orthogonal path with rounded corners that always detours around nodes- smart routing (
config.edges.smart, defaulttrue) — abezieredge whose route would run underneath another node automatically renders as a cleared orthogonal detour. Set per-edge viaEdgeDefinition.smart, or disable globally withconfig.edges.smart: false.
Components: FlowCanvas, ExpoFlowView, FlowCanvasControls
Hooks: useViewport, useCanvasControls
Utilities: buildSpawnedNode, buildInsertedNode, groupSelection,
ungroupGroup, deleteGroup, collectGroupSubtree, createIdAllocator,
serialization helpers and default style constants
Custom nodes: registerCustomNode, unregisterCustomNode
Types: GraphData, EdgeDefinition, NodeDefinition, FlowCanvasConfig,
LayoutRequest, event payloads, and more (see src/types).
See FEATURES.md for the complete feature reference.
# Android JVM unit tests
npm run test:android
# iOS pod tests (requires macOS + CocoaPods)
npm run test:iosMIT
