Live Demo
Line Chart
Data provided inline with data-props (JSON). Supports smooth curves, filled areas and per-series colors.
<div data-component="graph"
data-props='{
"type": "line",
"curve": true,
"fillArea": true,
"data": [
{"name": "2025", "color": "#3b82f6", "data": [
{"label": "Jan", "value": 65}, {"label": "Feb", "value": 59},
{"label": "Mar", "value": 80}, {"label": "Apr", "value": 81},
{"label": "May", "value": 76}, {"label": "Jun", "value": 90}
]},
{"name": "2024", "color": "#a855f7", "data": [
{"label": "Jan", "value": 45}, {"label": "Feb", "value": 52},
{"label": "Mar", "value": 58}, {"label": "Apr", "value": 61},
{"label": "May", "value": 70}, {"label": "Jun", "value": 72}
]}
]
}' style="height: 320px;">
</div>
Bar Chart from HTML Table
Point data-table to any table ID - the data is read directly from the table below (no JavaScript needed).
| Q1 | Q2 | Q3 | Q4 | |
|---|---|---|---|---|
| Sales | 120 | 150 | 135 | 180 |
| Expenses | 80 | 90 | 85 | 95 |
<table id="quarterly-table">
<thead>
<tr><th></th><th>Q1</th><th>Q2</th><th>Q3</th><th>Q4</th></tr>
</thead>
<tbody>
<tr><th>Sales</th><td>120</td><td>150</td><td>135</td><td>180</td></tr>
<tr><th>Expenses</th><td>80</td><td>90</td><td>85</td><td>95</td></tr>
</tbody>
</table>
<!-- Renders a bar chart from the table above -->
<div data-component="graph"
data-type="bar"
data-table="quarterly-table"
style="height: 320px;">
</div>
Pie Chart
Shows proportions with data labels. Use data-show-value-instead-of-percent to toggle between values and percentages.
<div data-component="graph"
data-type="pie"
data-show-value-instead-of-percent="false"
data-props='{
"data": [
{"name": "Traffic", "data": [
{"label": "Desktop", "value": 45},
{"label": "Mobile", "value": 38},
{"label": "Tablet", "value": 10},
{"label": "Others", "value": 7}
]}
]
}' style="height: 320px;">
</div>
Donut Chart
A ring chart with customizable thickness and center text - perfect for budgets and allocation.
<div data-component="graph"
data-type="donut"
data-donut-thickness="40"
data-center-text="Budget"
data-props='{
"data": [
{"name": "Budget", "data": [
{"label": "Housing", "value": 30},
{"label": "Food", "value": 25},
{"label": "Savings", "value": 20},
{"label": "Transport", "value": 15},
{"label": "Others", "value": 10}
]}
]
}' style="height: 320px;">
</div>
Gauge Chart
A single value in a range - great for KPIs and scores. Takes one series with one data point.
<div data-component="graph"
data-type="gauge"
data-max-gauge-value="100"
data-gauge-curve-width="24"
data-props='{
"data": [
{"name": "Performance", "data": [
{"label": "Score", "value": 78}
]}
]
}' style="height: 260px;">
</div>
Loading Data
From API URL
Set data-url to load data from a server endpoint. This chart fetches from api/sales.php which returns random
values on every request.
<div data-component="graph"
data-type="line"
data-url="api/sales.php"
style="height: 320px;">
</div>
<!-- Refresh manually from JavaScript -->
<script>
const element = document.querySelector('#url-graph');
const instance = GraphComponent.getInstance(element);
instance.refresh();
</script>
Auto Refresh (Polling)
Set data-polling-interval (ms) together with data-url to reload automatically. This chart polls every 5 seconds -
watch the values change.
<div data-component="graph"
data-type="bar"
data-url="api/sales.php"
data-polling-interval="5000"
style="height: 320px;">
</div>
Event-based Refresh
Charts sharing the same data-refresh-event all refresh together when that event is emitted through the EventManager.
<!-- Both charts listen for the same event -->
<div data-component="graph"
data-type="line"
data-url="api/sales.php"
data-refresh-event="graph:refresh-demo">
</div>
<div data-component="graph"
data-type="bar"
data-url="api/sales.php"
data-refresh-event="graph:refresh-demo">
</div>
<script>
// Emitting the event refreshes every listener at once
EventManager.emit('graph:refresh-demo');
</script>
JavaScript API
This chart is created entirely from JavaScript. Use the controls to call the API methods - every action is logged in the event log below.
Event Log
- Interact with the chart or buttons to see events...
// Create a graph entirely from JavaScript
const playground = await GraphComponent.create('#api-playground', {
type: 'line',
curve: true,
showLegend: true,
animation: true,
animationDuration: 800,
data: [
{name: 'Visitors', data: [
{label: 'Mon', value: 120},
{label: 'Tue', value: 150},
{label: 'Wed', value: 135}
]}
],
// Callbacks work too
onClick(dataPoint, seriesIndex) {
console.log('Clicked:', dataPoint.label, dataPoint.value);
},
onDataChange(data) {
console.log('Data updated:', data);
}
});
// Or grab an existing instance from an element / ID
const instance = GraphComponent.getInstance(element);
const same = GraphComponent.getInstance(instance.id);
// Replace the whole dataset
instance.setData([
{name: 'Visitors', data: [
{label: 'Mon', value: 200},
{label: 'Tue', value: 180},
{label: 'Wed', value: 220}
]}
]);
// Append a single point to a series (index 0 = first series)
instance.addDataPoint({label: 'Thu', value: 195}, 0);
// Switch chart type without recreating the chart
instance.setType('bar'); // line | bar | pie | donut | gauge
// Reload from the configured data-url (bypasses cache)
await instance.loadData();
// Refresh (same as loadData with force)
instance.refresh();
// Export as image (png, jpeg or svg)
instance.exportToImage('my-chart', 'png');
// Clean up when done
instance.destroy();
// DOM events are dispatched on the container element
const element = document.querySelector('#api-playground');
element.addEventListener('graph:created', e => {
console.log('Created:', e.detail.instance.id);
});
element.addEventListener('graph:loaded', e => {
console.log('Loaded from cache?', e.detail.fromCache);
});
element.addEventListener('graph:data-changed', e => {
console.log('New data:', e.detail.data);
});
element.addEventListener('graph:type-changed', e => {
console.log('Type changed to:', e.detail.type);
});
element.addEventListener('graph:error', e => {
console.error('Loading failed:', e.detail.error);
});
// The same events are also available through the EventManager
EventManager.on('graph:loaded', payload => {
console.log('Loaded:', payload.data);
});
Styling
Custom Colors & Legend Position
Pass a JSON array to data-colors for a custom palette and use data-legend-position (top, bottom, left, right) to
place the legend.
<!-- Custom color palette -->
<div data-component="graph"
data-type="donut"
data-colors='["#3b82f6", "#22c55e", "#f59e0b", "#ef4444"]'
data-props='{...}'>
</div>
<!-- Legend on the right -->
<div data-component="graph"
data-type="line"
data-legend-position="right"
data-curve="true"
data-props='{...}'>
</div>
Grid & Axis Options
Toggle the grid and axis with data-show-grid and data-show-axis, or restyle everything through container CSS.
<div data-component="graph"
data-type="line"
data-curve="true"
data-show-grid="false"
data-show-axis="false"
data-fill-area="true"
data-props='{...}'>
</div>
Key Features
Auto-initialization
Add data-component="graph" and the chart initializes itself - no JavaScript required.
5 Chart Types
Line, bar, pie, donut and gauge charts, all rendered as crisp SVG.
Multiple Data Sources
Inline data (data-props), HTML tables (data-table) or APIs (data-url).
Auto Refresh
Time-based polling and event-based refresh keep charts up to date.
Responsive
Charts redraw automatically when the container resizes.
Export
Export any chart as PNG, JPEG or SVG image with one call.
Interactive
Tooltips, legends and click handlers on every data point.
Event System
Rich lifecycle events via DOM events and the EventManager.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
data-type |
string | 'line' | Chart type: line, bar, pie, donut, gauge |
data-url |
string | - | API endpoint to load data from |
data-table |
string | - | ID of an HTML table to read data from |
data-props |
object | - | All options as inline JSON (including data) |
data-colors |
array | [...] | Custom color palette (JSON array) |
data-curve |
boolean | true | Smooth curve lines (line chart) |
data-fill-area |
boolean | false | Fill the area under the line (line chart) |
data-donut-thickness |
number | 30 | Ring thickness (donut chart) |
data-max-gauge-value |
number | 100 | Maximum value of the gauge range (gauge chart) |
data-gauge-curve-width |
number | 30 | Gauge arc thickness (gauge chart) |
data-center-text |
string | - | Text in the center (donut/gauge) |
data-show-grid |
boolean | true | Show grid lines |
data-show-axis |
boolean | true | Show axis lines |
data-show-legend |
boolean | true | Show legend |
data-legend-position |
string | 'bottom' | Legend position: top, bottom, left, right |
data-show-tooltip |
boolean | true | Show tooltips on hover |
data-show-data-labels |
boolean | true | Show labels on data points |
data-show-value-instead-of-percent |
boolean | true | Label style for pie/donut |
data-animation |
boolean | false | Enable draw animation |
data-animation-duration |
number | 1000 | Animation duration (ms) |
data-polling-interval |
number | 0 | Auto reload interval in ms (0 = off) |
data-refresh-event |
string | - | EventManager event(s) that trigger a refresh |
data-cache |
boolean | false | Cache data-url responses |
data-cache-time |
number | 60000 | Cache TTL in ms |
data-max-data-points |
number | 20 | Maximum visible data points |
data-autoload |
boolean | true | Load data on initialization |
How It Works
1. Declarative HTML
Every option can be set with data-* attributes. Values are parsed automatically - booleans, numbers, arrays and JSON objects all work.
2. Data Sources
Data comes from data-props, an HTML table via data-table, or a server endpoint via data-url - with optional caching.
3. SVG Rendering
The GraphRenderer draws pure SVG - sharp on every screen, themeable with CSS and exportable to image files.
4. Stays in Sync
ResizeObserver redraws on container changes, the MutationObserver picks up charts added to the DOM later, and locale changes trigger a refresh.
5. Events Everywhere
Each instance dispatches graph:* DOM events and mirrors them on the EventManager, so any part of your app can react.
6. Clean Lifecycle
Instances clean up their timers, observers and DOM references on destroy() - ready for SPA navigation.