Mind map auto layout
Position a tree of ideas automatically with a subtree-height layout and render it as curved SVG connectors in React.
React / Next.jsChartsmind-maptree-layoutsvgreact
Live preview
Code
export type Idea = { label: string; children?: Idea[] };
type Placed = { label: string; x: number; y: number; branch: number };
type Edge = { from: Placed; to: Placed };
const ROW = 44; // vertical slot per leaf
const COL = 190; // horizontal distance between levels
const W = 140; // node width
const H = 30; // node height
// Pass 1: how many leaf rows does each subtree need?
const rows = (n: Idea): number => (n.children?.length ? n.children.reduce((s, c) => s + rows(c), 0) : 1);
export function layout(root: Idea) {
const nodes: Placed[] = [];
const edges: Edge[] = [];
// Pass 2: place a node in the middle of the vertical slice reserved for its subtree.
const place = (n: Idea, depth: number, top: number, branch: number): Placed => {
const height = rows(n) * ROW;
const me: Placed = { label: n.label, x: depth * COL, y: top + height / 2, branch };
nodes.push(me);
let cursor = top;
n.children?.forEach((c, i) => {
const child = place(c, depth + 1, cursor, depth === 0 ? i : branch);
edges.push({ from: me, to: child });
cursor += rows(c) * ROW;
});
return me;
};
place(root, 0, 0, 0);
return { nodes, edges, width: Math.max(...nodes.map((n) => n.x)) + W, height: rows(root) * ROW };
}
const COLORS = ['#2563eb', '#db2777', '#059669', '#d97706', '#7c3aed', '#0891b2'];
export function MindMap({ root }: { root: Idea }) {
const { nodes, edges, width, height } = layout(root);
return (
<svg viewBox={`-8 -8 ${width + 16} ${height + 16}`} role="img" aria-label={`Mind map: ${root.label}`} className="w-full">
{edges.map((e, i) => {
const x1 = e.from.x + W, y1 = e.from.y, x2 = e.to.x, y2 = e.to.y, mx = (x1 + x2) / 2;
return <path key={i} d={`M${x1} ${y1}C${mx} ${y1} ${mx} ${y2} ${x2} ${y2}`} fill="none"
stroke={COLORS[e.to.branch % COLORS.length]} strokeWidth={2} />;
})}
{nodes.map((n, i) => (
<g key={i} transform={`translate(${n.x} ${n.y - H / 2})`}>
<rect width={W} height={H} rx={8} fill="#fff" stroke={COLORS[n.branch % COLORS.length]} strokeWidth={2} />
<text x={W / 2} y={H / 2 + 4} textAnchor="middle" fontSize={13} fill="#111827">{n.label}</text>
</g>
))}
</svg>
);
}
How it works
What it solves
Manually dragging every node of a mind map is tedious, and overlapping branches make the result unreadable. An automatic layout lets users type ideas and see a tidy tree immediately, which is the main reason people use these tools.
How it works
The data is a nested tree of { label, children }. layout runs two passes. The first pass measures each subtree: a leaf occupies one row of height ROW, and a parent occupies the sum of its children's heights, so siblings can never overlap vertically. The second pass assigns coordinates. Depth decides the x position (depth * COL), and each node is placed at the vertical centre of its own slice of space, which gives the balanced look of a mind map where parents sit midway between their first and last child.
The function returns a flat list of positioned nodes and a list of edges. The component draws each edge as a cubic Bezier path with horizontal control points, which makes smooth S-shaped connectors, and each node as a rounded rectangle with centred text. The SVG viewBox is computed from the largest coordinates so the diagram always fits.
Colours are chosen by the top-level branch index, making branches visually distinct with no extra data.
Adapting it
- Mirror half of the root's children to the left for a radial look.
- Measure text width with a canvas to size boxes dynamically.
- Store collapsed state on a node and skip its children in both passes.
Pitfalls
- Very wide trees need scrolling or zoom support.
- Labels are not wrapped; truncate or measure them.
How to use it
- Save as
components/MindMap.tsx(works on server or client). <MindMap root={{ label: 'Launch', children: [{ label: 'Design' }, { label: 'Build', children: [{ label: 'API' }] }] }} />.- Call
layout(root)directly to get coordinates for your own renderer. - Adjust
ROW,COLandWconstants for spacing.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.