Data Format
TopoKit reads a single JSON config describing your graph and how to render it. This page documents every field. The same JSON works in the SDK, in
TopoKit Play, and in the HTTP API (POST /api/topos).
Top-level shape
Two arrays carry the graph (nodes, edges). Every
other field is optional and configures presentation: layout, theme, built-in
UI components, and a few rendering knobs.
{
"nodes": [
{ "id": "a", "data": {}, "style": { "label": { "text": "A" } } },
{ "id": "b", "data": {}, "style": { "label": { "text": "B" } } },
{ "id": "c", "data": {}, "style": { "label": { "text": "C" } } }
],
"edges": [
{ "id": "e1", "source": "a", "target": "b", "data": {} },
{ "id": "e2", "source": "b", "target": "c", "data": {} }
],
"layout": "force",
"theme": "dark"
} | Field | Type | Default | Description |
|---|---|---|---|
nodes | Node[] | [] | Graph nodes. See Nodes. |
edges | Edge[] | [] | Graph edges. See Edges. |
layout | string | 'force' | Layout algorithm id. See Layouts. |
theme | string | 'dark' | Visual theme id. See Themes. |
directed | boolean | true | Whether edges have direction (draws arrowheads). |
allowSelfLoops | boolean | false | Permit edges where source === target. |
allowParallelEdges | boolean | false | Permit multiple edges between the same pair. |
fitPadding | number | 80 | Pixels of padding around the graph after fit-to-screen. |
autoFitOnResize | boolean | false | Re-fit when the container resizes. |
search | boolean | object | true | Search bar. See UI components. |
minimap | boolean | object | true | Bird-eye minimap. |
toolbar | boolean | object | true | Zoom controls / fit-to-screen. |
tooltip | boolean | object | true | Hover popup. |
contextMenu | boolean | object | true | Right-click menu. |
layoutSelector | boolean | object | true | Layout dropdown. |
themeSelector | boolean | object | true | Theme dropdown. |
infoPanel | boolean | object | true | Side panel showing selected node details. |
hoverFocus | boolean | object | true | Dim unrelated nodes on hover. |
legend | boolean | object | false | Color/shape key. Opt-in (set truthy to show). |
edgeBundling | boolean | 'fdeb' | 'merge' | object | false | Edge bundling. true auto-pivots fdeb→merge at ≥150 edges. See Edge Bundling. |
autoFitOnChange | boolean | true | Re-fit viewport on batch:end / focus:*. Suppressed during manual pan/zoom. |
hideOrphans | boolean | false | Hide non-combo nodes whose visible incident edge count is zero. |
autoCollapseCombosForce | boolean | false | Bypass the “user already collapsed something” guard for force layouts. |
comboEdgeAggregation | boolean | true | Synthesize cross-boundary combo edges. Set false to drop them. |
All booleans above are subject to the “defaults scrubbed” rule: toggling a key away from its default writes the explicit value; toggling back to the default removes the leaf from the saved JSON. This keeps the canonical form minimal — if a field isn't listed in your saved JSON, it's running with the default shown above.
Any UI-component field accepts false to disable, true
to enable with defaults, or an options object for per-component tuning.
legend is the only one that is off by default.
nodes
Each node is an object. Only id and data are required.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique identifier. |
data | object | yes | Arbitrary user data. Pass if you have none — used by tooltips, info panel, and any custom code. |
position | { x, y } | no | Initial coordinates. Layout overwrites these unless the node is locked. |
style | NodeStyle | no | Visual properties. See below. |
visible | boolean | no | Hide without removing. |
locked | boolean | no | Layout will not move this node. |
parent | string | no | Parent node id, for combo / group nodes. |
collapsed | boolean | no | If this is a combo, render it collapsed. |
NodeStyle
Field Type Default Description shape'circle' | 'rect' | 'rounded-rect' | 'diamond' | 'hexagon''circle'See Node shapes. radiusnumbertheme default (~20) For circle, the radius. For other shapes, the half-extent. widthnumberderived from radius Explicit width for rect-family shapes. heightnumberderived from radius Explicit height for rect-family shapes. fillstring (CSS color)theme default Fill color. strokestringtheme default Border color. strokeWidthnumber2Border width in px. opacitynumber (0–1)1Whole-node opacity. imagestring (URL)— Optional raster image rendered inside the node. imageWidthnumberauto Image width in px. imageHeightnumberauto Image height in px. imageRadiusnumber— Clip image with a circle of this radius. labelLabelStyle— Text label config (see below). badgesNodeBadge[][]Small badges anchored to corners (see below).
label (on NodeStyle)
Field Type Default Description textstring— The visible label text. If omitted, the node renders unlabeled. colorstringtheme default Text color. fontSizenumber12Pixel size. fontFamilystringtheme default CSS font-family. position'center' | 'top' | 'bottom' | 'left' | 'right'theme default Where the label sits relative to the node. visiblebooleantrueToggle without removing text.
NodeBadge
Field Type Default Description textstring— Badge text. Pair with icon or use alone. icon'dot' | 'alert' | 'check' | 'star'— Built-in icon to draw inside the badge. shape'circle' | 'rect''circle'Badge shape. position'top-right' | 'top-left' | 'bottom-right' | 'bottom-left''top-right'Anchor corner. colorstringtheme default Foreground (text / icon) color. backgroundstringtheme default Fill color. visiblebooleantrueToggle.
edges
Edges are always objects in TopoKit — there is no [src, tgt] tuple
short-form. Each edge needs an id so TopoKit can address it
individually, plus source, target, and a
data object (pass if none).
Field Type Required Description idstringyes Unique identifier. "e1", "a-b", anything stable. sourcestringyes Source node id (must exist in nodes). targetstringyes Target node id (must exist in nodes). dataobjectyes Arbitrary user data. Use if none. styleEdgeStyleno Visual properties (see below). visiblebooleanno Hide without removing.
EdgeStyle
Field Type Default Description type'straight' | 'bezier' | 'step''straight'Path style. See Edge types. strokestringtheme default Line color. strokeWidthnumber1.5Line width in px. opacitynumber (0–1)0.8Line opacity. arrowbooleanfollows top-level directed Draw an arrowhead at the target end. arrowSizenumber8Arrowhead size in px. dashedbooleanfalseDashed line. dashPatternnumber[]— Custom dash pattern, e.g. [4, 4]. animatedbooleanfalseAnimate dashes (flowing edge effect). animationSpeednumber1Multiplier for the dash animation. labelEdgeLabelStyle— Midpoint label (see below).
label (on EdgeStyle)
Field Type Default Description textstring— Label text rendered at the edge midpoint. colorstringtheme default Text color. backgroundstringtheme default Backing fill (helps readability over the line). fontSizenumber10Pixel size. fontFamilystringtheme default CSS font-family. visiblebooleanfalseEdge labels are off by default.
layout
String id of a layout algorithm. The renderer runs it on load and re-runs on demand.
Id Name When to use forceForce-directed General-purpose. Best default for unstructured networks. combo-forceCombo-force Force plus per-combo centroid spring and inter-combo repulsion. Use when nodes have parent set. hierarchicalHierarchical Org charts, dependency trees, workflows — layered top-down or left-right. dagreDagre Hierarchical with edge-crossing minimization. Good for DAGs with crossings. radialRadial Tree rooted at a node, drawn as concentric rings. circularCircular All nodes evenly spaced on one circle. Highlights connectivity patterns. concentricConcentric Group nodes onto concentric rings by a metric (degree, weight, etc.). gridGrid Uniform grid placement. Useful for small graphs and screenshots. autoAuto Pick the best layout from graph-shape heuristics. Good when you don't know.
theme
Id Best for lightDocumentation, print, embeds on light pages. darkDev tools, dashboards (default). corporateBusiness presentations, slide decks. neonMonitoring screens, telemetry walls. pastelEducational, soft visuals. monochromePrint, academic figures. oceanData dashboards with cool palette.
Each theme provides default fill, stroke, label color, and edge color. Anything
you set explicitly on NodeStyle / EdgeStyle overrides
the theme. Custom themes (defining your own palette) are supported through the
SDK API — not through the JSON config — see Core Concepts → Themes.
Node shapes
Id Name Notes circleCircle Default. Uses radius. rectRectangle Sharp corners. Uses width/height or radius. rounded-rectRounded rectangle Same as rect but with rounded corners. diamondDiamond 45°-rotated square. hexagonHexagon Regular six-sided polygon.
Edge types
Id Name Notes straightStraight Default. Direct line between endpoints. bezierBezier Smooth cubic curve. Looks good with force layouts. stepStep Right-angle orthogonal routing. Looks good with hierarchical / dagre.
UI components
Each component is configured by a top-level key. Pass true /
false to toggle, or an object to tune options. All except
legend are on by default.
Field What it shows Default searchTop-center search bar; filters/focuses nodes by label or id. on minimapBottom-left bird-eye overview with viewport rectangle. on toolbarBottom-right zoom controls and fit-to-screen button. on tooltipHover popup with node id and selected data fields. on contextMenuRight-click menu (Select neighbors / Hide by default). on layoutSelectorTop-left dropdown to switch layout algorithm at runtime. on* themeSelectorTop-right dropdown to switch theme at runtime. on infoPanelSlide-in side panel showing the selected node's full data. on hoverFocusDim unrelated nodes/edges when hovering one. on legendColor/shape key auto-derived from node and edge types. off
* layoutSelector only renders if the host page supplies a layout
runner (Play and the SDK quick-start both do).
Worked examples
Minimal — three nodes
JSON {
"nodes": [
{ "id": "a", "data": {}, "style": { "label": { "text": "A" } } },
{ "id": "b", "data": {}, "style": { "label": { "text": "B" } } },
{ "id": "c", "data": {}, "style": { "label": { "text": "C" } } }
],
"edges": [
{ "id": "e1", "source": "a", "target": "b", "data": {} },
{ "id": "e2", "source": "b", "target": "c", "data": {} }
],
"layout": "force",
"theme": "dark"
}
Org chart — dagre + light theme
JSON {
"nodes": [
{ "id": "ceo", "data": { "role": "CEO" }, "style": { "shape": "rounded-rect", "fill": "#3B82F6", "radius": 28, "label": { "text": "CEO", "color": "#fff" } } },
{ "id": "cto", "data": { "role": "CTO" }, "style": { "shape": "rounded-rect", "fill": "#10B981", "radius": 24, "label": { "text": "CTO", "color": "#fff" } } },
{ "id": "cfo", "data": { "role": "CFO" }, "style": { "shape": "rounded-rect", "fill": "#10B981", "radius": 24, "label": { "text": "CFO", "color": "#fff" } } },
{ "id": "eng-vp", "data": { "role": "VP Eng" }, "style": { "fill": "#8B5CF6", "label": { "text": "VP Eng", "color": "#fff" } } },
{ "id": "data-vp", "data": { "role": "VP Data" }, "style": { "fill": "#8B5CF6", "label": { "text": "VP Data", "color": "#fff" } } },
{ "id": "fin-mgr", "data": { "role": "Finance" }, "style": { "fill": "#F59E0B", "label": { "text": "Finance", "color": "#fff" } } },
{ "id": "eng-1", "data": { "role": "Backend" }, "style": { "fill": "#EC4899", "label": { "text": "Backend", "color": "#fff" } } },
{ "id": "eng-2", "data": { "role": "Frontend" }, "style": { "fill": "#EC4899", "label": { "text": "Frontend", "color": "#fff" } } },
{ "id": "data-1", "data": { "role": "Data Eng" }, "style": { "fill": "#06B6D4", "label": { "text": "Data Eng", "color": "#fff" } } },
{ "id": "data-2", "data": { "role": "ML" }, "style": { "fill": "#06B6D4", "label": { "text": "ML Eng", "color": "#fff" } } }
],
"edges": [
{ "id": "e1", "source": "ceo", "target": "cto", "data": {} },
{ "id": "e2", "source": "ceo", "target": "cfo", "data": {} },
{ "id": "e3", "source": "cto", "target": "eng-vp", "data": {} },
{ "id": "e4", "source": "cto", "target": "data-vp", "data": {} },
{ "id": "e5", "source": "cfo", "target": "fin-mgr", "data": {} },
{ "id": "e6", "source": "eng-vp", "target": "eng-1", "data": {} },
{ "id": "e7", "source": "eng-vp", "target": "eng-2", "data": {} },
{ "id": "e8", "source": "data-vp", "target": "data-1", "data": {} },
{ "id": "e9", "source": "data-vp", "target": "data-2", "data": {} }
],
"layout": "dagre",
"theme": "light",
"minimap": true,
"toolbar": true
}
Rich — telecom network, all the trimmings
JSON {
"nodes": [
{ "id": "core-1", "data": { "type": "core" }, "style": { "shape": "hexagon", "fill": "#7C3AED", "radius": 26, "label": { "text": "Core-A", "color": "#fff" }, "badges": [{ "icon": "alert", "background": "#EF4444", "position": "top-right" }] } },
{ "id": "core-2", "data": { "type": "core" }, "style": { "shape": "hexagon", "fill": "#7C3AED", "radius": 26, "label": { "text": "Core-B", "color": "#fff" } } },
{ "id": "edge-1", "data": { "type": "edge" }, "style": { "shape": "rect", "fill": "#0EA5E9", "label": { "text": "Edge-1", "color": "#fff" } } },
{ "id": "edge-2", "data": { "type": "edge" }, "style": { "shape": "rect", "fill": "#0EA5E9", "label": { "text": "Edge-2", "color": "#fff" } } },
{ "id": "edge-3", "data": { "type": "edge" }, "style": { "shape": "rect", "fill": "#0EA5E9", "label": { "text": "Edge-3", "color": "#fff" } } },
{ "id": "user-1", "data": { "type": "user" }, "style": { "fill": "#22C55E", "label": { "text": "User-1", "color": "#fff" } } },
{ "id": "user-2", "data": { "type": "user" }, "style": { "fill": "#22C55E", "label": { "text": "User-2", "color": "#fff" } } }
],
"edges": [
{ "id": "c1c2", "source": "core-1", "target": "core-2", "data": { "bw": "100G" }, "style": { "type": "bezier", "stroke": "#A78BFA", "strokeWidth": 3, "label": { "text": "100G" } } },
{ "id": "c1e1", "source": "core-1", "target": "edge-1", "data": {}, "style": { "type": "bezier" } },
{ "id": "c1e2", "source": "core-1", "target": "edge-2", "data": {}, "style": { "type": "bezier" } },
{ "id": "c2e3", "source": "core-2", "target": "edge-3", "data": {}, "style": { "type": "bezier" } },
{ "id": "e1u1", "source": "edge-1", "target": "user-1", "data": {}, "style": { "dashed": true } },
{ "id": "e2u2", "source": "edge-2", "target": "user-2", "data": {}, "style": { "dashed": true } }
],
"layout": "force",
"theme": "neon",
"directed": true,
"search": true,
"minimap": true,
"toolbar": true,
"tooltip": true,
"layoutSelector": true,
"themeSelector": true,
"legend": { "position": "bottom-left" },
"infoPanel": true,
"hoverFocus": true,
"fitPadding": 80
}
Versioning & evolution
TopoKit's JSON config is a living schema. New optional fields may be
added between SDK releases. Old fields remain backward-compatible.
- TopoKit Play (
/play) and shared topos (/t/<hash>) are dumb pipes. They pass your JSON straight to the SDK without validation — any field the current SDK accepts will work. - Unknown fields are silently ignored. Setting a field the current SDK doesn't recognize will not error; it just has no effect.
- This page is the contract surface. When the SDK gains new options, this page is the one that needs updating — not
/play or the share routes. - Runtime introspection. The SDK ships an authoritative manifest of layouts, themes, UI components, node shapes, and edge types:
import { OPTIONS } from '@topokit/core' or window.GraphKit.options in the IIFE bundle.
Tips for AI agents
If you are an LLM or automation pipeline converting user data to TopoKit JSON,
these heuristics work well.
- Start with the minimum. Produce a valid
nodes + edges blob first; layer in optional fields after. - Always include
data: {} on every node and edge — it is required, even if empty. - Default layout:
force for unknown graph shapes. Switch to dagre for clear parent-child structure; radial or hierarchical for explicit trees. - Default theme:
dark for technical / network graphs, light for org charts and document-like trees. - Adaptive UI: enable
search when nodes > 30; enable minimap when nodes > 50; enable legend when you set more than one distinct fill color. - Don't invent fields. Stick to the names in the tables above. Unknown keys are ignored, so they will not error — but they will not do anything either.
- Posting to the API:
POST https://topokit.io/api/topos with the JSON as body returns {"hash":"<short>"}. View at https://topokit.io/t/<hash>.
The TopoKit Play “Use with AI” modal contains
ready-to-copy prompts for the two most common workflows (paste-and-tweak vs.
agent-does-everything).
Next
- TopoKit Play — paste this JSON into a live editor.
- Core Concepts — the SDK-level graph model.
- Layouts — per-layout tuning options.
- API Reference — full SDK surface.