Drop in one JS file. Full CRUD API, SVG rendering, drag & drop, pan, zoom, and lossless PNG/SVG export — no build step required.
Try It
Click any node to select it. Use the panel below to add children, rename, or delete. Scroll to zoom, drag background to pan.
Features
A complete mind map solution in a single file. No bundler, no framework, no dependencies.
Pure SVG — crisp at any resolution, no canvas artifacts. Drop-shadow filters, colour-coded branches, smooth bezier links.
Drag individual nodes. Pan by dragging the background. Scroll-wheel zoom centred on the cursor. Pinch-to-zoom on touch.
Add, update, delete nodes programmatically. Insert a node between any two connected nodes without breaking the tree.
One-line export to high-DPI PNG or vector SVG. The export is pixel-perfect — no dependencies, pure web platform APIs.
Load any tree as JSON with fromJSON(). Snapshot the current state with toJSON() and save or share it.
Subscribe to nodeClick, nodeAdd, nodeUpdate, nodeDelete, nodeToggle and linkAdd to build reactive UIs on top.
layout: 'orgchart' draws managers above their reports, with collapsible branches. Typed nodes (person, team, company, project) get a colour, badge and job title. See the 40-person sample.
Dashed, labelled links between any two nodes for many-to-many ties: people to projects, advisors to companies. Portfolio sample.
MindkeeperMap.diff(before, after) finds who joined, left, moved or changed title; highlightDiff() marks them on the chart. January vs July sample.
Get Started
Two ways to add mindkeeper-map.js to your project.
<!-- Drop this in your HTML -->
<script src="https://cdn.jsdelivr.net/npm/mindkeeper-map/src/mindkeeper-map.js"></script>
<!-- Create a container -->
<div id="map" style="width:100%;height:500px"></div>
<!-- Initialise -->
<script>
const map = new MindkeeperMap('#map');
map.init({ topic: 'Root', children: [] });
</script>
# Install
npm install mindkeeper-map
// CommonJS
const MindkeeperMap = require('mindkeeper-map');
// Or copy the file directly from
// node_modules/mindkeeper-map/src/mindkeeper-map.js
// Initialise
const map = new MindkeeperMap('#map');
map.init(treeData);
API
All methods return this unless noted, so they chain.
Create an instance. selector is a CSS selector string or an Element. All options are optional:
{
layout: 'mindmap', // or 'orgchart' (top-down)
levelWidth: 240, // px between depth levels
levelHeight:110, // orgchart: px between levels
hSpacing: 24, // orgchart: gap between subtrees
stackLeaves:4, // orgchart: list 4+ leaf reports in a column
vSpacing: 54, // px between siblings
linkStroke: 3, // link line width
nodeBorder: 2.5, // node border width
fontFamily: 'system-ui'
}
Render the map from a tree object. Ids are optional — any node without one gets a stable auto-generated id.
map.init({
id: 'root',
topic: 'My Mind',
children: [
{ topic: 'Work', children: [
{ topic: 'Q3 launch' }
]},
{ topic: 'Personal' }
]
});
Add a child node to the given parent. Returns the new node's id.
const id = map.addNode('root', {
topic: 'New branch'
});
Insert a new node between a parent and one of its children. The existing child becomes a child of the new node.
// Before: Root → Work
const id = map.insertBetween(
'root', 'work',
{ topic: 'Category' }
);
// After: Root → Category → Work
Update a node's label. The map re-renders automatically.
map.updateNode(id, { topic: 'New label' });
Delete a node and all its descendants. Cannot delete the root node.
map.deleteNode(id);
Returns lightweight node info: { id, topic, children: string[] }.
const node = map.getNode('work');
// { id: 'work', topic: 'Work',
// children: ['q3', 'team'] }
fromJSON is an alias for init. toJSON returns the current tree as a plain JS object you can serialise.
const json = map.toJSON();
localStorage.setItem('map', JSON.stringify(json));
map.fromJSON(JSON.parse(saved));
Switch between 'mindmap' and 'orgchart', and fold branches. collapseAll() / expandAll() do every branch; collapsed nodes are saved as collapsed: true.
map.setLayout('orgchart');
map.collapse('engineering');
Cross-links outside the tree, drawn as dashed curves with a label. getLinks() lists them; they are saved in toJSON().links.
map.addLink('lin', 'apollo', 'leads');
Compare two trees by id: { added, removed, moved, changed, links, info }. highlightDiff rings the cards (green added, red removed, amber changed or moved) and adds a legend; clearDiff() removes it.
const diff = MindkeeperMap.diff(jan, jul);
map.init(jul).highlightDiff(diff);
Download the full mindmap as a high-DPI PNG or a clean vector SVG. No dependencies — built on XMLSerializer and Canvas 2D.
map.exportPNG('my-map.png');
map.exportSVG('my-map.svg');
Subscribe and unsubscribe from events. Returns this for chaining.
map
.on('nodeClick', ({ id, topic }) => {
console.log('Clicked:', topic);
})
.on('nodeAdd', ({ node, parentId }) => {
save(map.toJSON());
});
| Event | Payload | Description |
|---|---|---|
| nodeClick | { id, topic } | A node was clicked |
| nodeAdd | { node: { id, topic }, parentId } | A node was added via addNode or insertBetween |
| nodeUpdate | { id, data } | A node was updated via updateNode |
| nodeDelete | { id } | A node was deleted via deleteNode |
| nodeDeselect | {} | The background was clicked, clearing selection |
| nodeToggle | { id, collapsed } | A branch was collapsed or expanded |
| linkAdd / linkRemove | { link } / { from, to, type } | A cross-link was added or removed |
Data
The input and output format is a recursive tree. The id field is optional on input — the library generates stable ids for any node that omits it.
{
"id": "root",
"topic": "Project Plan",
"children": [
{
"id": "design",
"topic": "Design",
"children": [
{ "topic": "Wireframes" },
{ "topic": "Prototype" }
]
},
{
"id": "dev",
"topic": "Development",
"children": [
{ "topic": "API" },
{ "topic": "Frontend" }
]
}
]
}
Any node can carry "kind" (person, company, team, role, project, idea), "meta" (title, email, url, avatar, start, end, status) and "collapsed": true. The root can carry "links": [{ "from", "to", "type" }]. Maps without them render as before.
Export your mind map from the mindkeeper-mcp CLI or MCP server (~/.mindkeeper/mindmap.json, or its export_orgchart tool) and pass it directly to fromJSON().
Call toJSON() after any mutation to snapshot the tree and save it wherever you need — localStorage, a database, or a file.
map.on('nodeAdd', () =>
localStorage.setItem(
'map',
JSON.stringify(map.toJSON())
)
);