Connecting Real Data
All templates ship with realistic static sample data so you can preview them instantly. Replacing that data with your own source takes only a few lines of code.
## Find the data blocksEvery editable data section is marked with a /* DATA */ comment. Open js/charts.js and search for that marker:
/* DATA ─────────────────────────────────────────── */
const revenueData = {
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
actual: [42000, 38000, 51000, 62000, 58000, 71000],
target: [45000, 45000, 50000, 60000, 60000, 70000],
};
## REST API (fetch)
Replace the static object with an async fetch at page load:
async function loadData() {
const res = await fetch('/api/kpis/revenue?period=6m');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json(); // expects same shape as static object
}
document.addEventListener('DOMContentLoaded', async () => {
const data = await loadData();
revenueChart.setOption({
xAxis: { data: data.months },
series: [
{ data: data.actual },
{ data: data.target },
],
});
}); 💡 Use chart.setOption() to update an already-initialised ECharts instance without re-creating it. All animations and resize handlers are preserved.
For near-real-time dashboards, poll the API on an interval:
const REFRESH_MS = 30_000; // 30 seconds
async function refresh() {
const data = await loadData();
revenueChart.setOption({ series: [{ data: data.actual }] });
document.getElementById('kpi-revenue').textContent =
'$' + (data.actual.at(-1) / 1000).toFixed(1) + 'k';
}
refresh(); // immediate first load
setInterval(refresh, REFRESH_MS); // then every 30 s
## WebSocket (live streaming)
const ws = new WebSocket('wss://your-api.com/stream/kpis');
ws.addEventListener('message', ({ data }) => {
const point = JSON.parse(data); // { timestamp, value }
// Append new point, drop oldest
const opt = revenueChart.getOption();
const xData = [...opt.xAxis[0].data, point.timestamp].slice(-20);
const yData = [...opt.series[0].data, point.value].slice(-20);
revenueChart.setOption({
xAxis: [{ data: xData }],
series: [{ data: yData }],
});
});
## GraphQL
const QUERY = `
query RevenueKPIs($period: String!) {
revenue(period: $period) { months actual target }
}
`;
const res = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: QUERY, variables: { period: '6m' } }),
});
const { data } = await res.json();
## Handling loading and error states
const chartEl = document.getElementById('revenue-chart');
function setLoading(el, loading) {
if (loading) {
el.setAttribute('aria-busy', 'true');
el.style.opacity = '0.4';
} else {
el.removeAttribute('aria-busy');
el.style.opacity = '';
}
}
try {
setLoading(chartEl, true);
const data = await loadData();
revenueChart.setOption(buildOption(data));
} catch (e) {
chartEl.innerHTML = '<p class="dt-error">Failed to load data.</p>';
} finally {
setLoading(chartEl, false);
}
## Expected data shapes
Each template documents its expected data shape in a /* SCHEMA */ comment block at the top of js/charts.js. Match that shape from your API and no other changes are needed.