---
title: "JavaScript Charts: 30+ Chart Types, No Build Step"
description: "A JavaScript chart library with 30+ chart types: bar, line, area, pie, radar, treemap, sankey and heatmap, rendered in HTML/CSS and SVG with no canvas."
source: https://lemonadejs.com/charts/
---

# JavaScript charts

**One JavaScript chart library, thirty-plus chart types.** A bar chart or a
line chart renders in plain HTML + CSS. The browser reflows it, no
ResizeObserver, no redraw loop. Everything else, from a pie chart to a
treemap, is a single scaling SVG. Zero dependencies, no build step, and every
prop is reactive: change it, and only the dependent bindings re-render.

```bash
npm install @lemonadejs/charts@beta
```

<!--example-->

```js
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:

<!--example-->

```js
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:

<!--example-->

```js
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

| Family | Types |
|---|---|
| [Bars, lines & areas](/charts/docs/cartesian/) | bar, stackedbar (100%-stack), line, stackedarea, streamgraph, scatter, bubble, lollipop, dumbbell, waterfall, histogram, pareto, candlestick, ohlc, boxplot, arearange, columnrange |
| [Pies & radial](/charts/docs/pie-radial/) | pie, donut, radar, radialbar, polararea, gauge, funnel, pyramid |
| [Hierarchy & flow](/charts/docs/hierarchy-flow/) | treemap, sunburst, icicle, sankey, chord, arcdiagram |
| [Heatmap, bullet & more](/charts/docs/extras/) | heatmap, bullet, sparkline, packedbubble, pictogram, waffle, wordcloud |

Plus the cross-cutting features every type shares:
[palettes & colors](/charts/docs/palettes/),
[annotations, reference lines & axes](/charts/docs/annotations/), and
[events, drilldown & zoom](/charts/docs/interactivity/).

## Why it is this small

- **Bars are flexbox.** Bar and stacked charts are plain HTML + CSS
  (percentage heights in a flex row). Responsive on both axes with zero
  JavaScript. The browser does the layout, the chart never recomputes.
- **SVG scales itself.** Radial, hierarchy and flow types render one
  `<svg viewBox>`; the viewBox scales the scene natively, 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 reactive model is the engine's.** The chart is a regular LemonadeJS
  component: its scene is a computed model over live props. That is the
  whole architecture.

## Works everywhere

```js
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](/docs/react/) wraps any LemonadeJS block,
charts included.

**[Get started](/charts/docs/)**