A React-based mind map component with multiple layout strategies
A React-based mind map component with multiple layout strategies, theming support, and rich node features.
``bash`
npm install @clipmind/mindmapor
yarn add @clipmind/mindmapor
pnpm add @clipmind/mindmap
Make sure you have the following peer dependencies installed:
`bash`
npm install react react-dom mobx mobx-react styled-components slate slate-react slate-history
`tsx
import { useEffect, useState } from 'react';
import { MindMapEditor, generateStore, loadFromMarkdown } from '@clipmind/mindmap';
import styled from 'styled-components';
import type { MindMapStore } from '@clipmind/mindmap';
const Container = styled.div
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;;
export function MindmapDemo() {
const [store, setStore] = useState
useEffect(() => {
// generateStore is async to ensure fonts are loaded before layout calculation
generateStore().then(s => {
loadFromMarkdown('# Hello World\n\n## Item 1\n### Item 1.1\n## Item 2', s);
setStore(s);
} );
}, []);
if (!store) {
return (
);
}
return (
);
}
`
- 6 Layout Types: MindMap, Logic, Organization, Timeline, Tree, and Fishbone
- 50+ Color Themes: Light and dark themes with customization support
- Rich Nodes: Text, images, markers, checkboxes, and boundaries
- Export: SVG, PNG, JPG, and PDF export capabilities
- Undo/Redo: Full history management
- Markdown Import: Load mind maps from markdown text
- Serialization: Save and restore mind map state
---
Use generateStore to create a properly initialized MindMapStore. This function is async to ensure fonts are fully loaded before layout calculations, preventing text truncation issues.
`tsx
import { generateStore } from '@clipmind/mindmap';
// Basic usage
const store = await generateStore();
// With custom font
const store = await generateStore({ fontFamily: 'Inter' });
`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| fontFamily | string | 'Lexend' | Google Font to use for node text |
The following Google Fonts are supported:
Lexend, Roboto, Open Sans, Lato, Montserrat, Oswald, Source Sans Pro, Raleway, PT Sans, Merriweather, Inter, Roboto Mono, Fira Code, JetBrains Mono, Poppins, Quicksand, Playfair Display, Lora, Pacifico, Dancing Script
---
You can import mind map data from markdown text using the loadFromMarkdown function.
Use the ClipMind API to generate markdown from any text input.
> Get your API Token: Visit API Docs to obtain your API token.
`bash`
curl -X POST https://api.clipmind.com/public/mindmaps/markdown \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"text": "My MindMap\n- Idea 1\n- Idea 2",
"options": {
"mode": "summarize",
"language": "english",
"length": "medium"
}
}'
`tsx
import { loadFromMarkdown, generateStore } from '@clipmind/mindmap';
// Create store (async to ensure fonts are loaded)
const store = await generateStore();
// Fetch markdown from ClipMind API
const response = await fetch('https://api.clipmind.com/public/mindmaps/markdown', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${YOUR_API_TOKEN},
},
body: JSON.stringify({
text: 'React 入门指南',
options: {
mode: 'summarize',
language: 'chinese',
length: 'medium',
},
}),
});
const { data } = await response.json();
// Load markdown into store
loadFromMarkdown(data, store);
`
You can also load markdown directly without using the API:
`tsx# React 入门指南
const markdown =
;loadFromMarkdown(markdown, store);
`$3
The parser supports the following markdown syntax:
`markdown
Central Topic
Branch 1
- Item 1.1
- Item 1.2
-- Sub-item 1.2.1
Branch 2
- Item 2.1
`Heading levels:
-
# → Root node (level 1)
- ## → First-level branches (level 2)
- ### → Second-level branches (level 3)List items:
-
- → Child node
- -- → Grandchild node
- --- → Great-grandchild node
- Indentation also affects level: - Item (2 spaces = deeper level)$3
Include images with optional dimensions:
`markdown
My Mind Map
!logo{100x80} Brand
- !icon Feature 1
`Supported dimension formats:
-
{100x80} - width x height
- {width=100 height=80} - explicit attributes
- {width=100} - width only
- {100} - width only (shorthand)$3
Export with
convertMindMapToMarkdownWithMetadata preserves node styles:`markdown
Central Topic
Branch 1
`---
Serialization / Deserialization
Save and restore mind map state for persistence:
`tsx
// Save to localStorage
const data = await store.serialize();
localStorage.setItem('mindmap', JSON.stringify(data));// Restore from localStorage
const saved = localStorage.getItem('mindmap');
if (saved) {
store.deserialize(JSON.parse(saved));
}
`$3
`tsx
interface IMindMapData {
id: string;
rootNode: IMindMapNode;
theme: string; // Color theme ID
layout: {
type: ILayoutType; // 'mindmap' | 'logic' | 'org' | 'timeline' | 'tree' | 'fishbone'
theme: string; // Layout theme ID
};
resource?: IMindMapResource; // Images and other resources
createdAt?: string;
updatedAt?: string;
}
`---
Node Operations
$3
`tsx
// Add a child node
store.addChildNode({
title: 'New Child',
parentId: 'root', // Parent node ID
enableScrollToNode: true, // Auto-scroll to new node
});// Add a sibling node (same level as specified node)
store.addSiblingNode('node-id');
// Get the ID of the last added child
const newNodeId = store.getLastChildNodeId('parent-id');
`$3
`tsx
// Delete single node
store.deleteNodes(['node-id']);// Delete multiple nodes
store.deleteNodes(['node-1', 'node-2', 'node-3']);
// Note: Root node ('root') cannot be deleted
`$3
`tsx
// Set node text
store.setNodeText('node-id', 'New Title');// Set node text without saving to history
store.setNodeText('node-id', 'New Title', false);
// Update node image
store.updateNodeImage('node-id', {
src: 'https://example.com/image.png',
alt: 'Description',
box: { x: 0, y: 0, w: 100, h: 80 }
});
`$3
`tsx
// Select a node
store.setSelectedNodes(['node-id']);// Get selected nodes
const selected = store.selectedNodes;
// Clear selection
store.setSelectedNodes([]);
`---
Node Features
$3
Add icon markers to nodes:
`tsx
// Available markers: 'star', 'heart', 'flag', 'check', 'cross', 'question', etc.
store.addMarker('node-id', 'star');
store.removeMarker('node-id', 'star');
`$3
Add todo checkboxes to nodes:
`tsx
store.setNodeCheckbox('node-id', {
checked: false,
shape: 'square' // 'square' | 'circle' | 'diamond'
});// Toggle checkbox
store.toggleNodeCheckbox('node-id');
`$3
Add visual boundaries around node groups:
`tsx
store.setNodeBoundary('node-id', {
enabled: true,
style: 'rounded', // 'rounded' | 'square' | 'wave'
color: '#ff6b35'
});
`$3
Apply custom styles to individual nodes:
`tsx
store.setNodeCustomStyle('node-id', {
backgroundColor: '#ff6b35',
textColor: '#ffffff',
fontSize: 16,
fontWeight: 'bold'
});
`---
Undo / Redo
`tsx
// Check if operations are available
const canUndo = store.canUndo();
const canRedo = store.canRedo();// Perform undo/redo
store.undo();
store.redo();
`---
Image Export
$3
`tsx
store.saveSvg({
isCopy: false, // Copy to clipboard instead of download
exportCheckbox: true, // Include checkbox visuals
sealConfig: { // Optional watermark
style: 'corner',
text: 'My Brand'
}
});
`$3
`tsx
store.savePng({
scale: 2, // Export scale (1-4)
transparentBackground: false, // Transparent or themed background
isCopy: false, // Copy to clipboard
exportCheckbox: true,
targetWidth: 1920, // Optional fixed width
targetHeight: 1080 // Optional fixed height
});
`$3
`tsx
store.saveJpg({
scale: 2,
exportCheckbox: true,
targetWidth: 1920,
targetHeight: 1080
});
`$3
`tsx
store.savePdf({
scale: 2,
transparentBackground: false,
exportCheckbox: true
});
`$3
`tsx
// Get PNG as data URL (for thumbnails, previews)
const dataUrl = await store.savePngPreview({
scale: 1,
transparentBackground: false
});
`---
Layout Types
`tsx
// Set layout type and theme
store.setLayout('mindmap', 'mindmap-underline');
store.setLayout('logic', 'logic-minimal');
store.setLayout('org', 'org-corporate');
store.setLayout('timeline', 'timeline-default');
store.setLayout('tree', 'tree-trunk');
store.setLayout('fishbone', 'fishbone-classic');
`$3
| Layout Type | Available Themes |
|-------------|------------------|
|
mindmap | mindmap-underline, mindmap-modern, mindmap-organic, mindmap-creative |
| logic | logic-underline, logic-underline-stack, logic-minimal, logic-minimal-circle, logic-modern |
| org | org-corporate, org-modern |
| timeline | timeline-default, timeline-axis, timeline-horizontal |
| tree | tree-trunk, tree-structure |
| fishbone | fishbone-classic, fishbone-modern |---
Color Themes
$3
`tsx
import {
irisTheme,
neonTheme,
amethystTheme,
sunsetTheme,
oceanTheme,
forestTheme,
cherryBlossomTheme,
volcanoTheme,
hermesOrangeTheme,
tiffanyBlueTheme,
rainbowTheme,
premiumBlackGrayTheme,
roseGoldTheme,
deepSeaTheme,
lavenderTheme,
champagneTheme,
midnightBlueTheme,
emeraldTheme,
chinaRedTheme,
frenchFlagTheme,
germanFlagTheme,
americanFlagTheme,
japaneseFlagTheme,
roseTheme,
slateTheme,
goldTheme,
cyanTheme,
indigoTheme,
} from '@clipmind/mindmap';
`$3
`tsx
import {
darkIrisTheme,
darkNeonTheme,
darkAmethystTheme,
darkSunsetTheme,
darkOceanTheme,
darkForestTheme,
darkCherryBlossomTheme,
darkVolcanoTheme,
darkHermesOrangeTheme,
darkTiffanyBlueTheme,
darkRainbowTheme,
darkPremiumBlackGrayTheme,
darkRoseGoldTheme,
darkDeepSeaTheme,
darkLavenderTheme,
darkChampagneTheme,
darkMidnightBlueTheme,
darkEmeraldTheme,
darkChinaRedTheme,
darkFrenchFlagTheme,
darkGermanFlagTheme,
darkAmericanFlagTheme,
darkJapaneseFlagTheme,
darkRoseTheme,
darkSlateTheme,
darkGoldTheme,
darkCyanTheme,
darkIndigoTheme,
} from '@clipmind/mindmap';
`$3
`tsx
import { AVAILABLE_THEMES, LIGHT_THEMES, DARK_THEMES } from '@clipmind/mindmap';// Get all themes
console.log(AVAILABLE_THEMES.length); // 56 themes
// Apply a theme
store.colorTheme = darkNeonTheme;
// Reset node colors after theme change
store.resetNodeThemes();
`---
Canvas Operations
`tsx
// Fit mind map to screen
store.canvas.fitToScreen();// Zoom controls
store.canvas.zoomIn();
store.canvas.zoomOut();
store.canvas.setScale(1.5);
// Get current view state
const view = store.canvas.getView();
// { x: number, y: number, scale: number }
`---
Complete Example: ClipMind API Integration
> Get your API Token: Visit API Docs to obtain your API token.
`tsx
import { useState, useEffect } from 'react';
import {
MindMapStore,
MindMapEditor,
generateStore,
loadFromMarkdown,
} from '@clipmind/mindmap';// Get your token from https://clipmind.tech/workspace
const API_TOKEN = 'your-api-token';
function MindMapDemo() {
const [store, setStore] = useState(null);
useEffect(() => {
// generateStore is async to ensure fonts are loaded before layout calculation
generateStore().then(setStore);
}, []);
const handleGenerateFromAPI = async () => {
if (!store) return;
const response = await fetch('https://api.clipmind.com/public/mindmaps/markdown', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization':
Bearer ${API_TOKEN},
},
body: JSON.stringify({
text: 'React 入门指南',
options: {
mode: 'brainstorm',
language: 'chinese',
length: 'medium',
},
}),
}); const { data } = await response.json();
loadFromMarkdown(data, store);
};
const handleExport = () => {
store?.savePng({ scale: 2 });
};
const handleSave = async () => {
if (!store) return;
const data = await store.serialize();
localStorage.setItem('mindmap', JSON.stringify(data));
};
const handleLoad = () => {
const saved = localStorage.getItem('mindmap');
if (saved && store) {
store.deserialize(JSON.parse(saved));
}
};
if (!store) {
return
Loading...;
} return (
);
}
``