How to Embed Interactive Charts in Confluence with HTML Macro
Confluence is where a lot of teams track project status, document KPIs, and share team metrics. But when it comes to actually visualizing that data, the options are limited. You can paste a static image (which goes stale), use a third-party integration (which requires a separate subscription), or accept that your Confluence page just won't have charts.
HTML Macro for Confluence opens a fourth option: embed Chart.js directly inside a Confluence page. The charts are interactive (hover for tooltips, click to toggle series), render instantly, and live alongside your documentation — no external service required.
This guide covers four chart types with copy-paste code for each.
Before You Start
Install HTML Macro for Confluence from the Atlassian Marketplace. It's free and runs on Atlassian Forge.
Security note: The examples below load Chart.js from a CDN (cdn.jsdelivr.net). Your Confluence admin needs to add that domain to the HTML Macro whitelist. Go to Confluence Settings → HTML Macro → Security Settings and add https://cdn.jsdelivr.net to the allowed domains.
Once that's done, add the macro to any Confluence page (Insert → Macro → HTML Macro), paste the code, and use the live preview to verify it before saving.
Bar Chart — Team Metrics or Sprint Data
Good for: comparing values across categories — issues by status, story points per sprint, bugs per team member.
<canvas id="barChart" width="700" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('barChart'), {
type: 'bar',
data: {
labels: ['To Do', 'In Progress', 'In Review', 'Done'],
datasets: [{
label: 'Issues',
data: [12, 8, 5, 34],
backgroundColor: ['#94a3b8', '#3b82f6', '#f59e0b', '#22c55e'],
borderRadius: 6,
}]
},
options: {
responsive: false,
plugins: {
legend: { display: false },
title: {
display: true,
text: 'Issue Status — Sprint 42',
font: { size: 15, weight: '600' },
color: '#1e293b',
}
},
scales: {
y: {
beginAtZero: true,
grid: { color: '#f1f5f9' },
ticks: { color: '#64748b' }
},
x: {
grid: { display: false },
ticks: { color: '#64748b' }
}
}
}
});
</script>
Replace the labels and data arrays with your actual values. Each label maps to the corresponding data value by index.
Line Chart — Velocity Trend or Weekly Progress
Good for: showing trends over time — sprint velocity, bug discovery rate, weekly active users, anything with a time axis.
<canvas id="lineChart" width="700" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('lineChart'), {
type: 'line',
data: {
labels: ['Sprint 37', 'Sprint 38', 'Sprint 39', 'Sprint 40', 'Sprint 41', 'Sprint 42'],
datasets: [{
label: 'Story Points Completed',
data: [41, 38, 45, 52, 49, 55],
borderColor: '#6366f1',
backgroundColor: 'rgba(99, 102, 241, 0.08)',
borderWidth: 2,
pointBackgroundColor: '#6366f1',
pointRadius: 4,
fill: true,
tension: 0.3,
}]
},
options: {
responsive: false,
plugins: {
legend: { position: 'top' },
title: {
display: true,
text: 'Sprint Velocity — Last 6 Sprints',
font: { size: 15, weight: '600' },
color: '#1e293b',
}
},
scales: {
y: {
beginAtZero: false,
grid: { color: '#f1f5f9' },
ticks: { color: '#64748b' }
},
x: {
grid: { display: false },
ticks: { color: '#64748b' }
}
}
}
});
</script>
Pie Chart — Issue Type or Priority Distribution
Good for: showing how a total is distributed — issue types, budget allocation, time spent by category. Keep it to five segments or fewer.
<canvas id="pieChart" width="500" height="400"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('pieChart'), {
type: 'pie',
data: {
labels: ['Bug', 'Feature', 'Task', 'Tech Debt', 'Spike'],
datasets: [{
data: [18, 32, 25, 14, 11],
backgroundColor: [
'#ef4444',
'#6366f1',
'#3b82f6',
'#f59e0b',
'#94a3b8',
],
borderWidth: 2,
borderColor: '#fff',
}]
},
options: {
responsive: false,
plugins: {
legend: { position: 'right' },
title: {
display: true,
text: 'Open Issues by Type',
font: { size: 15, weight: '600' },
color: '#1e293b',
}
}
}
});
</script>
Doughnut Chart — Goal Progress or Completion Status
Good for: showing a single metric against a target — sprint completion, budget consumed, onboarding progress.
<div style="position:relative; width:400px; margin:0 auto;">
<canvas id="doughnutChart" width="400" height="400"></canvas>
<div style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center; font-family:sans-serif;">
<div style="font-size:36px; font-weight:700; color:#1e293b;">72%</div>
<div style="font-size:13px; color:#64748b;">Complete</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('doughnutChart'), {
type: 'doughnut',
data: {
datasets: [{
data: [72, 28],
backgroundColor: ['#6366f1', '#f1f5f9'],
borderWidth: 0,
cutout: '78%',
}]
},
options: {
responsive: false,
plugins: {
legend: { display: false },
tooltip: { enabled: false },
}
}
});
</script>
Change the 72 and 28 values (they must add to 100) and update the center label to match.
Multi-Dataset Line Chart — Compare Two Teams or Products
When you want to compare two data series on the same time axis:
<canvas id="multiLineChart" width="700" height="350"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart(document.getElementById('multiLineChart'), {
type: 'line',
data: {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6'],
datasets: [
{
label: 'Team Alpha',
data: [12, 19, 15, 22, 18, 25],
borderColor: '#6366f1',
backgroundColor: 'transparent',
borderWidth: 2,
pointRadius: 4,
tension: 0.3,
},
{
label: 'Team Beta',
data: [8, 14, 20, 17, 24, 21],
borderColor: '#22c55e',
backgroundColor: 'transparent',
borderWidth: 2,
pointRadius: 4,
tension: 0.3,
}
]
},
options: {
responsive: false,
plugins: {
legend: { position: 'top' },
title: {
display: true,
text: 'Weekly Issues Resolved',
font: { size: 15, weight: '600' },
color: '#1e293b',
}
},
scales: {
y: { beginAtZero: true, grid: { color: '#f1f5f9' } },
x: { grid: { display: false } }
}
}
});
</script>
Tips for Production Use
Hard-code the data for now. Chart.js in Confluence can't call your Jira API or database — the data lives in the HTML. If your metrics change weekly, treat the Confluence page as a weekly snapshot: update the arrays when you update the commentary.
Use width and height attributes directly on <canvas>. Don't rely on responsive: true inside Confluence — the macro iframe has fixed dimensions and responsive sizing can behave unexpectedly.
Each chart needs a unique id. If you put multiple charts on the same page, give each canvas a different id (barChart, lineChart, etc.) to avoid conflicts.
Test in the live preview before saving. HTML Macro's side-by-side preview shows you exactly what Confluence page visitors will see — including CDN scripts loading.
A Note on the CDN Whitelist
Your admin needs to whitelist https://cdn.jsdelivr.net in HTML Macro's security settings for the Chart.js scripts to load. If the charts appear blank, that's the most common cause. Check the browser console for a CSP error and confirm the domain is in the allowlist.
For environments where all external CDNs are blocked, you can paste the minified Chart.js source directly into the <script> tag — it's about 60KB minified, which is manageable.
Install HTML Macro for Confluence — free on the Atlassian Marketplace
Top comments (0)