Your first React graph
Render three service dependencies with the Graph component. React handles mounting and cleanup; your application owns the data.
Expected result: three blue nodes in a circle, with arrows from Gateway to Orders and from Orders to Database.
Open the live React tutorialPreview availability
Requires repository access, Node.js 22.13+ and pnpm 11.7.0 for local setup. Packages are not published to npm. Project status.
1. Prepare the workspace
Graphora's packages are unpublished. From an authorized checkout, build and run the checked-in React example against the local workspace packages:
cd graph-library
pnpm install --frozen-lockfile
pnpm build
pnpm exec vite examples/react/first-graph \
--config examples/react/vite.config.ts2. Render a graph
Keep the graph and layout objects stable. Graph treats a new graph reference as a data update and a new layout reference as a runtime configuration change. Defining static values outside the component is the simplest safe pattern.
These snippets come directly from the runnable tutorial, so no copying is required to run it. Keep examples/react/vite.config.ts and inspect examples/react/first-graph/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Graphora React graph</title>
</head>
<body>
<h1>Service dependencies</h1>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>The complete examples/react/first-graph/src/main.tsx:
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import { createCircularLayout } from '@graphora/layouts'
import { Graph } from '@graphora/react'
import './style.css'
const graph = {
nodes: [
{ id: 'gateway', label: 'Gateway' },
{ id: 'orders', label: 'Orders' },
{ id: 'database', label: 'Database' }
],
edges: [
{
id: 'gateway-orders',
source: 'gateway',
target: 'orders',
directed: true
},
{
id: 'orders-database',
source: 'orders',
target: 'database',
directed: true
}
]
}
const layout = createCircularLayout({ radius: 120 })
const theme = {
backgroundColor: '#f8fafc',
node: { fill: '#2563eb', radius: 22 },
edge: { stroke: '#64748b', strokeWidth: 2, arrow: { end: true } },
label: { visible: true, fill: '#0f172a', fontSize: 13 }
}
function App(): React.ReactElement {
return (
<Graph
graph={graph}
layout={layout}
fitToViewOnLayout={{ padding: 48 }}
ariaLabel="Service dependency graph"
className="graph-surface"
theme={theme}
/>
)
}
createRoot(document.querySelector('#root') as HTMLElement).render(<App />)The component enables hover, click selection, node drag, background pan, and wheel zoom by default. It destroys its engine and renderer when React unmounts it.
The container size comes from examples/react/first-graph/src/style.css:
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
.graph-surface {
width: 100%;
height: 100%;
min-height: 360px;
}
#graph,
#root {
height: 360px;
}
body {
font-family: system-ui, sans-serif;
background: #f8fafc;
color: #0f172a;
}
h1 {
font-size: 1rem;
margin: 12px 16px;
}Make your first change
In src/main.tsx, change the theme's node fill from #2563eb to #0f766e and save. Vite refreshes the page: all three nodes become teal, while their labels and connections stay the same. This changes the default node color; item styles can override individual nodes.
Your graph should show Gateway → Orders → Database. If the canvas is empty, check the container height and browser console; see blank canvas troubleshooting.
Continue with your own graph
- Load your own data: replace the sample nodes and edges.
- Choose a layout: change how nodes are arranged.
- Style nodes and edges: encode meaning with color.
- Add interactions: select, drag, pan and zoom.
Data that changes during rendering
Use useMemo when graph or layout values depend on state. Stable identity prevents an unrelated render from reloading data or rebuilding the runtime.
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import { createCircularLayout } from '@graphora/layouts'
import { Graph } from '@graphora/react'
import './style.css'
const sourceGraph = {
nodes: [
{ id: 'gateway', label: 'Gateway' },
{ id: 'orders', label: 'Orders' },
{ id: 'database', label: 'Database' }
],
edges: [
{ id: 'go', source: 'gateway', target: 'orders' },
{ id: 'od', source: 'orders', target: 'database' }
]
}
function FilterableGraph(): React.ReactElement {
const [showDatabase, setShowDatabase] = React.useState(true)
const layout = React.useMemo(() => createCircularLayout({ radius: 120 }), [])
const graph = React.useMemo(() => {
const nodes = showDatabase
? sourceGraph.nodes
: sourceGraph.nodes.filter((node) => node.id !== 'database')
const visibleIds = new Set(nodes.map((node) => node.id))
return {
nodes,
edges: sourceGraph.edges.filter(
(edge) => visibleIds.has(edge.source) && visibleIds.has(edge.target)
)
}
}, [showDatabase])
return (
<>
<button onClick={() => setShowDatabase((visible) => !visible)}>
Toggle database
</button>
<Graph
graph={graph}
layout={layout}
graphUpdatePolicy="preserve"
className="graph-surface"
ariaLabel="Filterable service dependencies"
/>
</>
)
}
createRoot(document.querySelector('#root') as HTMLElement).render(
<FilterableGraph />
)The default graphUpdatePolicy="relayout" runs the current layout when the graph reference changes. Use graphUpdatePolicy="preserve" for filtering or incremental updates where existing node positions and the camera should survive. Call the ref's clearPositionMemory() before an explicit reset.
Add callbacks and a ref
Use controlled selection when another panel also needs to select graph items. The React callbacks example shows onNodeClick, onCanvasClick, onSelectionChange, onReady, and GraphRef. The dependency explorer guide shows a more complete application.
See GraphProps and GraphRef in the React API reference.