@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
chart.js: the whole page
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

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.

type is a state
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.

streaming points
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.

Thirty chart types, one import.