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, and nodeDelete to build reactive UIs on top.
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:
{
levelWidth: 240, // px between depth levels
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));
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 |
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" }
]
}
]
}
Export your mind map from the mindkeeper-mcp CLI or MCP server (~/.mindkeeper/mindmap.json) 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())
)
);