@lemonadejs/charts · 30+ chart types
JavaScript charts in HTML, CSS and SVG
Bars are flexbox. Everything else is one scaling SVG. No canvas, no redraw loop, no dependencies, and every prop is reactive: change it and only the dependent bindings re-render.
- Zero dependencies
- MIT licensed
- No build step
- Inspectable DOM
import { html, mount } from 'lemonadejs';
import Charts from '@lemonadejs/charts';
const series = [
{ name: 'Product A', data: [120, 190, 80, 220] },
{ name: 'Product B', data: [80, 105, 130, 140] },
];
const App = () => html`<${Charts} type="bar"
categories="${['Q1', 'Q2', 'Q3', 'Q4']}"
series="${series}" legend labels />`;
mount(App, document.getElementById('app')); ● live: this exact code, running on this page
Every type, one prop away
The same series and categories drive every chart.
Sixteen of the thirty-plus types, rendered live below; type is the
only thing that changes between them.
type="bar" Stacked bar type="stackedbar" Line type="line" Stacked area type="stackedarea" Scatter type="scatter" Waterfall type="waterfall" Pie type="pie" Donut type="donut" Radar type="radar" Gauge type="gauge" Funnel type="funnel" Treemap type="treemap" Sunburst type="sunburst" Sankey type="sankey" Heatmap type="heatmap" Waffle type="waffle" Cross-cutting and shared by every type: palettes & colors, annotations, reference lines & axes, events, drilldown & zoom.
One data definition, every chart
Switching charts is changing one word. And because type is a live
prop like any other, you can switch it at runtime: pick a type below.
const App = (props, { state }) => {
const type = state('line'); // a live prop: change it, the chart follows
return html`<div>
<select onchange="${(e) => (type.value = e.target.value)}">
<option value="line">line</option>
<option value="bar">bar</option>
<option value="stackedarea">stacked area</option>
<option value="heatmap">heatmap</option>
</select>
<${Charts} type="${type}" categories="${months}"
series="${series}" smooth legend />
</div>`;
}; ● live
Live by construction
Props are states. Assign to a state and the chart updates. There is no
chart.update() and no imperative object to keep in sync with your data.
const App = (props, { state }) => {
const series = state([{ name: 'Live', data: [30, 45, 38, 52, 44] }]);
const push = () => {
const data = series.value[0].data.slice(-19);
data.push(Math.round(30 + Math.random() * 40));
series.value = [{ name: 'Live', data }]; // assign: the chart updates
};
return html`<div>
<button onclick="${push}">Add point</button>
<${Charts} type="line" series="${series}" smooth
markers="${false}" legend="${false}" height="${220}" />
</div>`;
}; ● live
Why it is this small
Bars are flexbox
Bar and stacked charts are plain HTML and CSS: percentage heights in a flex row. Responsive on both axes with zero JavaScript; the browser does the layout.
SVG scales itself
Radial, hierarchy and flow types render one <svg viewBox>. The viewBox scales the scene, so there is no resize handling anywhere in the package.
No canvas
Nothing rasterizes. Everything is inspectable DOM you can style with CSS, test with selectors and screen-read.
The engine's reactive model
The chart is a regular LemonadeJS component: a computed scene over live props. Works by value, by name, or as the <lm-charts> custom element in any framework.