Charts

One block, thirty-plus chart types. Bars and lines render in plain HTML + CSS. The browser reflows them, no ResizeObserver, no redraw loop. Everything else is a single scaling SVG. Zero dependencies, no build step, and every prop is reactive: change it, and only the dependent bindings re-render.

npm install @lemonadejs/charts@beta
live
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 />`;

One data definition, every chart

The same series + categories pair drives every type, so switching charts is changing one word, and because type is a live prop, you can switch at runtime too:

live
import Charts from '@lemonadejs/charts';

const series = [
    { name: 'Users', data: [42, 51, 60, 55, 71, 68, 84, 90, 82, 95, 102, 110] },
    { name: 'Sessions', data: [30, 34, 41, 38, 45, 52, 58, 55, 62, 60, 71, 75] },
];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

const App = (props, { state }) => {
    const type = state('line');
    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 markers="${false}" legend />
    </div>`;
};

Live by construction

Props are states. Assign to a state and the chart updates. There is no chart.update(), no imperative object to keep in sync:

live
import Charts from '@lemonadejs/charts';

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 }];
    };
    return html`<div>
        <button onclick="${push}">Add point</button>
        <${Charts} type="line" series="${series}" markers="${false}"
            smooth legend="${false}" height="${240}" />
    </div>`;
};

Every type, one prop away

FamilyTypes
Bars, lines & areasbar, stackedbar (100%-stack), line, stackedarea, streamgraph, scatter, bubble, lollipop, dumbbell, waterfall, histogram, pareto, candlestick, ohlc, boxplot, arearange, columnrange
Pies & radialpie, donut, radar, radialbar, polararea, gauge, funnel, pyramid
Hierarchy & flowtreemap, sunburst, icicle, sankey, chord, arcdiagram
Heatmap, bullet & moreheatmap, bullet, sparkline, packedbubble, pictogram, waffle, wordcloud

Plus the cross-cutting features every type shares: palettes & colors, annotations, reference lines & axes, and events, drilldown & zoom.

Why it is this small

Works everywhere

import { html, mount, setComponents, createWebComponent } from 'lemonadejs';
import Charts from '@lemonadejs/charts';
import '@lemonadejs/charts/style.css';

html`<${Charts} />`            // by value, no registration
setComponents({ Charts });     // <Charts /> by name anywhere
createWebComponent(Charts);    // <lm-charts> in plain HTML or any framework

React users: the React adapter wraps any LemonadeJS block, charts included.

Get started