Chrome Extension Concept Map Builder: A Developer’s Guide
Concept maps are powerful tools for visualizing relationships between ideas. Building a Chrome extension that enables users to create, edit, and export concept maps directly from their browser opens up numerous possibilities for knowledge workers, researchers, and developers. This guide walks you through the architecture, key components, and implementation details for creating a functional concept map builder as a Chrome extension.
Understanding the Core Architecture
A Chrome extension concept map builder consists of three primary components: the popup interface, the background service worker, and the content script for page interaction. The popup handles the primary user interface where users create and manipulate nodes. The background worker manages data persistence and handles communication between different parts of the extension.
The data model for a concept map is straightforward. Each node represents a concept, and edges represent relationships between concepts. A simple JSON structure captures this:
const conceptMap = {
nodes: [
{ id: "node-1", label: "Chrome Extension", x: 100, y: 100 },
{ id: "node-2", label: "Concept Map", x: 300, y: 200 },
{ id: "node-3", label: "JavaScript", x: 500, y: 100 }
],
edges: [
{ from: "node-1", to: "node-2", label: "creates" },
{ from: "node-1", to: "node-3", label: "uses" }
]
};
This structure allows you to serialize the concept map easily for storage and export.
Setting Up the Extension Structure
Every Chrome extension requires a manifest file. For a concept map builder targeting modern Chrome versions, use Manifest V3:
{
"manifest_version": 3,
"name": "Concept Map Builder",
"version": "1.0",
"description": "Create and manage concept maps from your browser",
"permissions": ["storage", "activeTab"],
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"host_permissions": ["<all_urls>"]
}
The popup.html file serves as your primary interface. Include a canvas element for rendering the concept map and toolbar buttons for common actions like adding nodes, connecting them, and exporting the result.
Implementing the Canvas Rendering
The rendering engine is the heart of your concept map builder. HTML5 Canvas provides excellent performance for interactive diagrams. Create a dedicated JavaScript module that handles node rendering, edge drawing, and user interactions.
class ConceptMapRenderer {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.nodes = [];
this.edges = [];
this.selectedNode = null;
this.dragging = null;
this.setupEventListeners();
}
setupEventListeners() {
this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
}
render() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawEdges();
this.drawNodes();
}
drawNodes() {
this.nodes.forEach(node => {
this.ctx.fillStyle = node.selected ? '#4a90d9' : '#ffffff';
this.ctx.fillRect(node.x - 50, node.y - 25, 100, 50);
this.ctx.strokeRect(node.x - 50, node.y - 25, 100, 50);
this.ctx.fillStyle = '#333333';
this.ctx.fillText(node.label, node.x, node.y + 5);
});
}
drawEdges() {
this.edges.forEach(edge => {
const fromNode = this.nodes.find(n => n.id === edge.from);
const toNode = this.nodes.find(n => n.id === edge.to);
if (fromNode && toNode) {
this.ctx.beginPath();
this.ctx.moveTo(fromNode.x, fromNode.y);
this.ctx.lineTo(toNode.x, toNode.y);
this.ctx.stroke();
}
});
}
}
This renderer supports basic node selection, dragging, and edge visualization. Extend it with double-click handlers for node editing and zoom controls for larger concept maps.
Managing State and Persistence
Chrome’s storage API provides reliable persistence for your concept maps. Use chrome.storage.local for extension-specific data:
class ConceptMapStore {
static async saveMap(mapId, data) {
return new Promise((resolve, reject) => {
chrome.storage.local.set({ [mapId]: data }, () => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve();
}
});
});
}
static async loadMap(mapId) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(mapId, (result) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(result[mapId] || null);
}
});
});
}
static async listMaps() {
return new Promise((resolve) => {
chrome.storage.local.get(null, (result) => {
resolve(Object.keys(result));
});
});
}
}
This storage layer enables users to save multiple concept maps and resume their work across browser sessions.
Adding Export Functionality
Users often need to export their concept maps for presentations or documentation. Implement multiple export formats:
function exportAsJSON(mapData) {
const blob = new Blob([JSON.stringify(mapData, null, 2)], {
type: 'application/json'
});
downloadBlob(blob, 'concept-map.json');
}
function exportAsPNG(canvas) {
canvas.toBlob((blob) => {
downloadBlob(blob, 'concept-map.png');
});
}
function exportAsSVG(nodes, edges) {
let svg = '<svg xmlns="http://www.w3.org/2000/svg">';
edges.forEach(edge => {
svg += `<line x1="${edge.from.x}" y1="${edge.from.y}" x2="${edge.to.x}" y2="${edge.to.y}" stroke="black"/>`;
});
nodes.forEach(node => {
svg += `<rect x="${node.x-50}" y="${node.y-25}" width="100" height="50" fill="white" stroke="black"/>`;
svg += `<text x="${node.x}" y="${node.y+5}" text-anchor="middle">${node.label}</text>`;
});
svg += '</svg>';
const blob = new Blob([svg], { type: 'image/svg+xml' });
downloadBlob(blob, 'concept-map.svg');
}
function downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
Integrating with Web Content
A powerful feature for a concept map builder is the ability to extract content from web pages. Use content scripts to scrape headings and key terms:
// content.js
function extractPageContent() {
const headings = Array.from(document.querySelectorAll('h1, h2, h3'))
.map(h => h.textContent.trim());
const links = Array.from(document.querySelectorAll('a'))
.map(a => ({
text: a.textContent.trim(),
href: a.href
}))
.filter(l => l.text && l.href);
return { headings, links };
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'extractContent') {
const content = extractPageContent();
sendResponse(content);
}
});
This enables users to quickly generate concept maps from research materials or organize their browsing findings.
Performance Optimization Tips
For larger concept maps, implement viewport culling to only render visible nodes. Use requestAnimationFrame for smooth drag interactions. Consider using Web Workers for complex layout algorithms if you implement automatic node positioning.
function renderFrame() {
if (this.needsRender) {
this.render();
this.needsRender = false;
}
requestAnimationFrame(this.renderFrame.bind(this));
}
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one