Props
<ForceGraph<TNode, TEdge>> accepts the following props. TNode must extend BaseNode { id: string } and TEdge must extend BaseEdge { id: string; source: string; target: string }.
Array of nodes to render. Each must have a unique
id.Array of edges connecting nodes by their
id.Text rendered below each node.
Drives degree-based collision radius and clustering hub priority.
Text rendered on the midpoint of the edge. An empty string hides the label.
Fires after the camera centres on the node.
Fires after the camera centres on the edge midpoint.
Fires on canvas background click.
Skip Louvain detection and the cluster-aware forces. The rest of the simulation (charge, collision, link spring) is untouched.
Render a live zoom-percentage indicator at the given corner.
Replaces the container class entirely.
Container
aria-label.Rendered when
nodes is empty and isLoading is falsy.Rendered while the canvas lib is dynamic-imported, or when
nodes is empty and isLoading is true.Sets
aria-busy on the container.Imperative handle — see below.
Imperative handle
Grab a ref to drive the graph from outside React state.Animate to an absolute zoom level.
Fit every node in view.
Pan to graph coordinates.
Stop the simulation.
Resume the simulation.
Force a single canvas repaint.
Clustering
Enabled by default. On eachnodes or edges change, the renderer runs Louvain community detection and feeds the result into the simulation as:
- Variable link distance — intra-community links pull to
theme.cluster.intraLinkDistance(120by default); inter-community bridges pull totheme.cluster.interLinkDistance(280). - Centroid force — every tick each node is nudged toward its community’s centroid at strength
theme.cluster.strength · alpha.
graphology + graphology-communities-louvain add ~60 KB gzipped. Both are bundled into the package — no peer dependency to install.
Data
The package does no fetching. Pull data with whatever you already use (TanStack Query, SWR, RSC, plainfetch) and pass arrays straight in.
TNode must extend BaseNode { id: string }; TEdge must extend BaseEdge { id: string; source: string; target: string }. Carry any domain fields on the node or edge directly.
Sub-path exports
| Import path | What’s there |
|---|---|
@crosmos/graph | ForceGraph, BaseNode, BaseEdge, theme types, DEFAULT_THEME, mergeTheme. |
@crosmos/graph/mock | 500-node mock dataset. |
@crosmos/graph/styles.css | Container and zoom-indicator default styles. |
SSR and Next.js
The renderer wrapsreact-force-graph-2d, which is canvas-based and client-only. <ForceGraph> is marked "use client" and dynamic-imports the lib internally, so it’s safe to mount inside Next.js server components — the container renders its loadingState (or nothing) on the server and hydrates on the client.
Don’t import
@crosmos/graph from a server-only module. It needs the browser to actually draw.