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 }.
TNode[]
required
Array of nodes to render. Each must have a unique
id.TEdge[]
required
Array of edges connecting nodes by their
id.(node: TNode) => string
default:"n.label ?? n.name ?? n.id"
Text rendered below each node.
(node: TNode) => number
default:"n.weight ?? 0"
Drives degree-based collision radius and clustering hub priority.
(edge: TEdge) => string
default:"e.label ?? \"\""
Text rendered on the midpoint of the edge. An empty string hides the label.
(node: TNode) => void
Fires after the camera centres on the node.
(edge: TEdge) => void
Fires after the camera centres on the edge midpoint.
() => void
Fires on canvas background click.
boolean
default:"false"
Skip Louvain detection and the cluster-aware forces. The rest of the simulation (charge, collision, link spring) is untouched.
false | "top-right" | "top-left" | "bottom-right" | "bottom-left"
default:"false"
Render a live zoom-percentage indicator at the given corner.
string
default:"\"cg-root\""
Replaces the container class entirely.
string
default:"\"Knowledge graph\""
Container
aria-label.ReactNode
default:"\"No entities to display\""
Rendered when
nodes is empty and isLoading is falsy.ReactNode
Rendered while the canvas lib is dynamic-imported, or when
nodes is empty and isLoading is true.boolean
Sets
aria-busy on the container.Ref<ForceGraphHandle>
Imperative handle — see below.
Imperative handle
Grab a ref to drive the graph from outside React state.(scale: number, durationMs?: number) => void
Animate to an absolute zoom level.
(durationMs?: number, paddingPx?: number) => void
Fit every node in view.
(x: number, y: number, durationMs?: number) => void
Pan to graph coordinates.
() => void
Stop the simulation.
() => void
Resume the simulation.
() => void
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
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.