
By Yamuno Team
16 Jun 2026
6 min read
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.
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.
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.
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>
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>
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.
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>
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.
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
Featured App
Embed custom HTML, CSS, and JavaScript directly inside Confluence pages
Get product updates and tips straight to your inbox.
No spam, ever.
Confluence doesn't have a built-in project status dashboard. Here's how to build one that shows RAG indicators, progress bars, countdown timers, and key metrics — using HTML Macro for Confluence.
Read moreProduct managers need a different view of Jira than engineering teams do. Here's how to set up charts and dashboards that track the metrics that actually matter for product decisions.
Read moreConfluence's native PDF export has no cover page. Here's how to add a professional title page — with your logo, accent color, and subtitle — to any Confluence PDF export.
Read more