Knowledge Graph
A force-directed knowledge graph built from Markdown or structured records: a constellation-style dark canvas you can drag, zoom and search, with each node's source citations in a side panel.
This is a WebberUI Pro component
The install command below needs a licence to run; without one it returns 401.
Turn concepts scattered across documents into a graph you can explore. Feed it Markdown and # headings become document nodes, ## headings become concepts, [[wiki links]] connect them and #tags form their own group — or pass structured nodes and links directly. On the dark constellation canvas, node size follows degree and color follows type; drag to pan, scroll to zoom around the cursor and search as you type. Selecting a node pans the camera to center it and opens a source panel with its summary, every citation's source file and location, and neighbor chips you can jump to. The force simulation is self-written, with no d3 dependency.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add "https://webberui.com/r/knowledge-graph.en.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<KnowledgeGraph/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add "https://webberui.com/r/knowledge-graph.en.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/knowledge-graph.
Usage
import { KnowledgeGraph, parseMarkdownGraph } from "@/components/ui/knowledge-graph";
const notes = `# Release Process
The standard steps from merge to production; every step must support a [[Rollback]]. #operations
## Rollback
Keep build artifacts for the last three versions and purge the [[Caching Strategy]] in one step. #operations
`;
// Pass Markdown directly
<KnowledgeGraph markdown={notes} onCitationClick={(c) => console.log(c.source, c.location)} />;
// Or convert first, then add structured records such as people
const graph = parseMarkdownGraph(notes);
<KnowledgeGraph
nodes={[...graph.nodes, { id: "Jo", label: "Jo", type: "person" }]}
links={[...graph.links, { source: "Jo", target: "Rollback", weight: 2 }]}
defaultSelectedId="Rollback"
/>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
nodes | KnowledgeGraphNode[] | [] | Nodes; ignored when markdown is given |
links | KnowledgeGraphLink[] | [] | Links (undirected); links to missing nodes or self-links are skipped |
markdown | string | — | Markdown source; when given, it is converted with parseMarkdownGraph |
selectedId | string | null | — | Controlled selected node id; null means nothing selected |
defaultSelectedId | string | null | null | Initially selected node in uncontrolled mode |
onSelectedChange | (id: string | null) => void | — | Called when the selection changes; null when deselected |
typeColors | Partial<Record<KnowledgeNodeType, string>> | See CSS variables below | Node color per type |
onCitationClick | (citation: KnowledgeCitation, node: KnowledgeGraphNode) => void | — | Called when a citation in the source panel is clicked; without it citations are display-only |
height | number | 480 | Canvas height (px) |
showLabels | "auto" | "always" | "never" | "auto" | auto: shown at zoom ≥ 0.8 or for highly connected nodes; always: all; never: only the hovered / focused and selected node |
linkDistance | number | 70 | Ideal link length (px); the actual length is this divided by the square root of the weight |
className | string | — | Passed through to the root container |
KnowledgeGraphNode
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique node id, referenced by links |
label | string | — | Display name, also the text search matches against |
type | "doc" | "concept" | "person" | "tag" | "concept" | Node type, which decides its color and filter chip |
summary | string | — | Summary shown in the source panel |
citations | KnowledgeCitation[] | — | Source citations |
KnowledgeCitation
| Field | Type | Default | Description |
|---|---|---|---|
source | string | — | Source file name or document title |
excerpt | string | — | Quoted paragraph (the panel shows up to three lines) |
location | string | — | Location, such as a heading path and line number |
KnowledgeGraphLink
| Field | Type | Default | Description |
|---|---|---|---|
source | string | — | Node id at one end |
target | string | — | Node id at the other end |
weight | number | 1 | Weight: higher means a thicker, shorter link |
Also exports parseMarkdownGraph(md: string): KnowledgeGraphData plus the KnowledgeGraphProps, KnowledgeGraphData and KnowledgeNodeType types.
CSS variables
| Variable | Default | Description |
|---|---|---|
--wb-knowledge-graph-doc | #e5e5e5 | Document node color |
--wb-knowledge-graph-concept | #2dd4bf | Concept node color |
--wb-knowledge-graph-person | #fbbf24 | Person node color |
--wb-knowledge-graph-tag | #a3a3a3 | Tag node color |
--wb-knowledge-graph-duration | 200ms | How long other nodes take to fade on hover and search |
How it works
- Markdown rules:
#headings → document nodes;##headings → concept nodes linked to their document, and###and deeper hang under the nearest##;[[Name]](or[[Name|shown text]]) links to the node with the same name, creating a concept if missing; a#tagmust follow line start, whitespace or CJK punctuation, soa#binside a URL is not a tag. Each paragraph becomes a citation whoselocationreads "Document › Heading · line N", and the concepts and tags it links to receive the same citation; repeated pairs add up their weight - Self-written force simulation: initial positions are placed in a disc from a hash of each id (identical on SSR and client); every tick applies O(n²) repulsion (strength -120), link springs (ideal length
linkDistance, shorter for heavier links), gravity of 0.02 toward the center and 0.85 velocity damping. Alpha decays from 1 by 0.97 andrequestAnimationFramestops below 0.005. Up to about 300 nodes is recommended - Re-layout only on structural changes: as long as node ids and links stay the same, passing fresh arrays every render does not restart the layout; when nodes are added, existing ones keep their positions
- Camera: panning and zooming use a spring (stiffness 300, damping 35) within a 0.3–3 zoom range; until the user moves the camera, it keeps framing the whole graph while the layout settles. The simulation pauses while the canvas is offscreen
- Dragging and pinning: a dragged node follows the pointer and reheats the graph to alpha 0.3, then is released on pointer up; double-click a node to pin it (drawn with a dashed ring) and again to unpin; "Re-run layout" clears every pin and restarts from the initial positions
- Search: matching nodes stay lit while the rest fade to 0.2; Enter selects and centers the first match, and an Enter pressed during IME composition is ignored
Accessibility
- The root is
role="application"witharia-describedbypointing atsr-onlyinstructions, plus ansr-onlylist of every node and its connections, so screen reader users get the whole graph without operating the canvas - Each node is a focusable
role="button"with anaria-labelsuch as "Concept: Caching Strategy, 6 connections" andaria-pressedwhile selected; Tab visits nodes from most to least connected, and focus highlights neighbors like hover does and brings the node into view - With the canvas focused, arrow keys pan and
+/-zoom; Enter or Space selects a node and Esc clears the selection, which is announced througharia-live - Toolbar buttons all have an
aria-label, type filter chips expose their state witharia-pressed, and every control has afocus-visiblering - When the system "reduce motion" setting is on, the simulation pre-runs 300 ticks synchronously and renders statically, the flowing dots along links are skipped, camera moves jump instead of animating, the source panel fades without sliding, and dragging moves only that node without reheating the graph
System Map Canvas
A pannable, zoomable system map: nodes and edges are generated from data, groups expand and collapse, and selecting a node highlights its connected paths and opens its details.
Custom Video Player
A video player with a custom control bar that fades out automatically during playback, supporting scrub-to-seek, volume, fullscreen, and keyboard operation.