Mermaid vs D3.js vs Chart.js: Diagrams and Data Visualization (2026)
TL;DR
Mermaid generates diagrams from text — flowcharts, sequence diagrams, Gantt charts, ER diagrams, all from Markdown-like syntax, no coding needed, used in GitHub, Notion, and docs. D3.js is the low-level data visualization library — binds data to DOM/SVG, full control over every pixel, powers the NYT and Observable, steep learning curve. Chart.js is the simple charting library — responsive charts with minimal code, canvas-based, 8 chart types built-in, great for dashboards. In 2026: Mermaid for documentation diagrams, D3.js for custom/complex visualizations, Chart.js for standard dashboard charts.
Key Takeaways
- Mermaid: ~3M weekly downloads — text-to-diagram, Markdown, no JS coding needed
- D3.js: ~5M weekly downloads — full-control SVG/DOM, custom visualizations
- Chart.js: ~5M weekly downloads — simple API, canvas, 8 chart types, responsive
- Completely different tools: diagrams (Mermaid), custom viz (D3), charts (Chart.js)
- Mermaid requires no JavaScript — just text definitions
- D3 has the steepest learning curve but maximum flexibility
Mermaid
Mermaid — text-to-diagram:
Flowchart
```mermaid
flowchart TD
A[Start] --> B{Is it working?}
B -- Yes --> C[Great!]
B -- No --> D[Debug]
D --> E[Fix the bug]
E --> B
C --> F[Deploy]
### Sequence diagram
```markdown
```mermaid
sequenceDiagram
participant Client
participant API
participant Database
Client->>API: POST /api/packages
API->>Database: INSERT package
Database-->>API: OK
API-->>Client: 201 Created
Client->>API: GET /api/packages
API->>Database: SELECT * FROM packages
Database-->>API: [packages]
API-->>Client: 200 [packages]
### ER diagram
```markdown
```mermaid
erDiagram
USER ||--o{ PACKAGE : creates
PACKAGE ||--|{ VERSION : has
PACKAGE ||--o{ DOWNLOAD : tracks
USER {
string id PK
string name
string email
}
PACKAGE {
string id PK
string name
string description
string userId FK
}
VERSION {
string id PK
string version
date publishedAt
string packageId FK
}
### JavaScript API
```typescript
import mermaid from "mermaid"
// Initialize:
mermaid.initialize({
startOnLoad: true,
theme: "dark",
securityLevel: "loose",
})
// Render programmatically:
const { svg } = await mermaid.render("diagram-1", `
flowchart LR
A[npm install] --> B[Build]
B --> C[Test]
C --> D[Deploy]
`)
document.getElementById("output").innerHTML = svg
D3.js
D3.js — data-driven documents:
Bar chart
import * as d3 from "d3"
const data = [
{ name: "react", downloads: 25000000 },
{ name: "vue", downloads: 5000000 },
{ name: "svelte", downloads: 2000000 },
{ name: "solid", downloads: 500000 },
]
const width = 600, height = 400, margin = { top: 20, right: 20, bottom: 40, left: 80 }
const svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
const x = d3.scaleLinear()
.domain([0, d3.max(data, d => d.downloads)!])
.range([margin.left, width - margin.right])
const y = d3.scaleBand()
.domain(data.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(0.2)
svg.selectAll("rect")
.data(data)
.join("rect")
.attr("x", margin.left)
.attr("y", d => y(d.name)!)
.attr("width", d => x(d.downloads) - margin.left)
.attr("height", y.bandwidth())
.attr("fill", "#3b82f6")
// Add axes:
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d => `${d / 1e6}M`))
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
Interactive line chart
import * as d3 from "d3"
const data = [
{ date: new Date("2026-01"), value: 100 },
{ date: new Date("2026-02"), value: 150 },
{ date: new Date("2026-03"), value: 130 },
// ...
]
const x = d3.scaleTime()
.domain(d3.extent(data, d => d.date) as [Date, Date])
.range([margin.left, width - margin.right])
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)!])
.range([height - margin.bottom, margin.top])
const line = d3.line<typeof data[0]>()
.x(d => x(d.date))
.y(d => y(d.value))
.curve(d3.curveMonotoneX)
svg.append("path")
.datum(data)
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "#3b82f6")
.attr("stroke-width", 2)
// Add tooltip on hover:
svg.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => x(d.date))
.attr("cy", d => y(d.value))
.attr("r", 4)
.attr("fill", "#3b82f6")
.on("mouseover", (event, d) => {
tooltip.style("opacity", 1)
.html(`${d.date.toLocaleDateString()}: ${d.value}`)
.style("left", `${event.pageX + 10}px`)
.style("top", `${event.pageY - 10}px`)
})
Treemap
import * as d3 from "d3"
const data = {
name: "npm",
children: [
{ name: "react", value: 25000000 },
{ name: "next", value: 8000000 },
{ name: "vue", value: 5000000 },
{ name: "express", value: 30000000 },
{ name: "axios", value: 50000000 },
],
}
const root = d3.treemap<typeof data>()
.size([width, height])
.padding(2)(
d3.hierarchy(data).sum(d => d.value)
)
svg.selectAll("rect")
.data(root.leaves())
.join("rect")
.attr("x", d => d.x0)
.attr("y", d => d.y0)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0)
.attr("fill", (_, i) => d3.schemeTableau10[i])
Chart.js
Chart.js — simple charting:
Bar chart
import { Chart } from "chart.js/auto"
new Chart(document.getElementById("chart") as HTMLCanvasElement, {
type: "bar",
data: {
labels: ["react", "vue", "svelte", "solid"],
datasets: [{
label: "Weekly Downloads (M)",
data: [25, 5, 2, 0.5],
backgroundColor: ["#61dafb", "#42b883", "#ff3e00", "#446b9e"],
borderRadius: 6,
}],
},
options: {
responsive: true,
plugins: {
title: { display: true, text: "Framework Downloads" },
legend: { display: false },
},
scales: {
y: { beginAtZero: true, title: { display: true, text: "Downloads (M)" } },
},
},
})
Line chart with multiple datasets
import { Chart } from "chart.js/auto"
new Chart(canvas, {
type: "line",
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
datasets: [
{
label: "react",
data: [22, 23, 24, 24, 25, 25],
borderColor: "#61dafb",
tension: 0.3,
},
{
label: "vue",
data: [4.5, 4.6, 4.8, 4.9, 5.0, 5.1],
borderColor: "#42b883",
tension: 0.3,
},
],
},
options: {
responsive: true,
interaction: { mode: "index", intersect: false },
plugins: {
title: { display: true, text: "Download Trends (2026)" },
},
},
})
Doughnut / Pie chart
import { Chart } from "chart.js/auto"
new Chart(canvas, {
type: "doughnut",
data: {
labels: ["npm", "pnpm", "yarn", "bun"],
datasets: [{
data: [60, 25, 10, 5],
backgroundColor: ["#cb3837", "#f69220", "#2c8ebb", "#fbf0df"],
borderWidth: 0,
}],
},
options: {
responsive: true,
plugins: {
title: { display: true, text: "Package Manager Market Share" },
},
},
})
React wrapper
import { Bar, Line, Doughnut } from "react-chartjs-2"
import { Chart, registerables } from "chart.js"
Chart.register(...registerables)
function DownloadsChart() {
return (
<Bar
data={{
labels: ["react", "vue", "svelte"],
datasets: [{
label: "Downloads (M)",
data: [25, 5, 2],
backgroundColor: ["#61dafb", "#42b883", "#ff3e00"],
}],
}}
options={{
responsive: true,
plugins: { legend: { display: false } },
}}
/>
)
}
Feature Comparison
| Feature | Mermaid | D3.js | Chart.js |
|---|---|---|---|
| Purpose | Text-to-diagram | Custom data viz | Standard charts |
| Rendering | SVG | SVG/DOM | Canvas |
| Input | Text/Markdown | Data + code | Data + config |
| Coding required | ❌ (text only) | ✅ (complex) | ✅ (simple) |
| Chart types | Diagrams only | Unlimited | 8 built-in |
| Flowcharts | ✅ | ✅ (manual) | ❌ |
| Sequence diagrams | ✅ | ❌ | ❌ |
| ER diagrams | ✅ | ❌ | ❌ |
| Interactive | Basic | ✅ (full control) | ✅ (tooltips, zoom) |
| Animations | ❌ | ✅ | ✅ |
| Responsive | ✅ | Manual | ✅ |
| Learning curve | Low | High | Low |
| React bindings | Via dangerouslySetInnerHTML | Manual | react-chartjs-2 |
| Used by | GitHub, Notion, docs | NYT, Observable | Dashboards |
| Weekly downloads | ~3M | ~5M | ~5M |
When to Use Each
Use Mermaid if:
- Creating diagrams for documentation (flowcharts, sequence, ER)
- Want to define diagrams in Markdown (GitHub renders natively)
- No JavaScript coding needed
- Building docs with VitePress, Docusaurus, or similar
Use D3.js if:
- Need fully custom visualizations (no template constraints)
- Building interactive data explorers or dashboards
- Need maps, treemaps, force graphs, or novel chart types
- Want complete control over every SVG element
Use Chart.js if:
- Need standard charts quickly (bar, line, pie, doughnut, radar)
- Building admin dashboards or analytics pages
- Want responsive charts with minimal code
- Using React (react-chartjs-2)
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on Mermaid v11.x, D3.js v7.x, and Chart.js v4.x.
Compare visualization libraries and frontend tooling on PkgPulse →