Data Storytelling

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.

How to install Pro components →See the plans →

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>"

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

480
70
200
Active edited usageDefault props
<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

PropTypeDefaultDescription
nodesKnowledgeGraphNode[][]Nodes; ignored when markdown is given
linksKnowledgeGraphLink[][]Links (undirected); links to missing nodes or self-links are skipped
markdownstring—Markdown source; when given, it is converted with parseMarkdownGraph
selectedIdstring | null—Controlled selected node id; null means nothing selected
defaultSelectedIdstring | nullnullInitially selected node in uncontrolled mode
onSelectedChange(id: string | null) => void—Called when the selection changes; null when deselected
typeColorsPartial<Record<KnowledgeNodeType, string>>See CSS variables belowNode 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
heightnumber480Canvas 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
linkDistancenumber70Ideal link length (px); the actual length is this divided by the square root of the weight
classNamestring—Passed through to the root container

KnowledgeGraphNode

FieldTypeDefaultDescription
idstring—Unique node id, referenced by links
labelstring—Display name, also the text search matches against
type"doc" | "concept" | "person" | "tag""concept"Node type, which decides its color and filter chip
summarystring—Summary shown in the source panel
citationsKnowledgeCitation[]—Source citations

KnowledgeCitation

FieldTypeDefaultDescription
sourcestring—Source file name or document title
excerptstring—Quoted paragraph (the panel shows up to three lines)
locationstring—Location, such as a heading path and line number
FieldTypeDefaultDescription
sourcestring—Node id at one end
targetstring—Node id at the other end
weightnumber1Weight: higher means a thicker, shorter link

Also exports parseMarkdownGraph(md: string): KnowledgeGraphData plus the KnowledgeGraphProps, KnowledgeGraphData and KnowledgeNodeType types.

CSS variables

VariableDefaultDescription
--wb-knowledge-graph-doc#e5e5e5Document node color
--wb-knowledge-graph-concept#2dd4bfConcept node color
--wb-knowledge-graph-person#fbbf24Person node color
--wb-knowledge-graph-tag#a3a3a3Tag node color
--wb-knowledge-graph-duration200msHow 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 #tag must follow line start, whitespace or CJK punctuation, so a#b inside a URL is not a tag. Each paragraph becomes a citation whose location reads "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 and requestAnimationFrame stops 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" with aria-describedby pointing at sr-only instructions, plus an sr-only list 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 an aria-label such as "Concept: Caching Strategy, 6 connections" and aria-pressed while 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 through aria-live
  • Toolbar buttons all have an aria-label, type filter chips expose their state with aria-pressed, and every control has a focus-visible ring
  • 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