# Coming from 1.x

> Your Chartist 1.x charts keep running on v2. Here's what stays the same, what's new, and how to move to the functions one chart at a time.

Good news first: your 1.x code keeps working. v2 adds a functional API next to the classes you know, and both run on the same engine. Upgrade, check your events (one line, see below), and move to the functions whenever a chart needs something new.

## What keeps working

The classes are still there, imported the same way, with the same constructor arguments: the container, the data, the options and the responsive options.

**1.x code on v2** (runs in the browser):

`src/main.ts`:

```ts
// Chartist 1.x code, running on v2 unchanged.
import 'chartist/styles.css';
import { LineChart } from 'chartist';

new LineChart(
  '#chart',
  {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    series: [
      [12, 9, 7, 8, 5],
      [2, 1, 3.5, 7, 3]
    ]
  },
  {
    fullWidth: true,
    chartPadding: { right: 40 }
  }
);
```

`index.html`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script type="module" src="/src/main.ts"></script>
  </head>
  <body>
    <div id="chart" class="ct-golden-section"></div>
  </body>
</html>
```

- **`LineChart`, `BarChart` and `PieChart`** from `chartist`, with `update(data, options, override)`, `detach()`, `on()` and `off()`. The first render still happens on the next tick, and charts still redraw when the window resizes, and now also when their container does.
- **Every other 1.x export**, from `chartist` and from the UMD global: `Svg`, `SvgList`, `SvgPath`, `easings`, `Interpolation`, the axis classes, `EventEmitter`, the helpers and the types (`LineChartData`, `BarChartOptions` and the rest). None of them was removed. The classes and what only they use are [deprecated](#deprecated-in-2x-removed-in-30), and still work through all of 2.x.
- **The look.** Same class names, same default styles. v2 is tested against screenshots of 1.5.0, and the default stylesheet matches them.
- **Your stylesheet imports.** `chartist/dist/index.css` and the Sass entry `chartist/dist/index.scss` are still in the package. The new short path is `chartist/styles.css`:

```ts
import 'chartist/styles.css';
import { BarChart, LineChart, PieChart } from 'chartist';
```

## Events need the compat plugin

This is the one change you may need to make. In 1.x, every chart emitted `data`, `optionsChanged`, `draw`, `created`, `animationBegin` and `animationEnd`. In v2 these 1.x events live in an opt-in plugin, so charts that don't use them don't carry the code.

Add `compat` from `chartist/compat` to `plugins`, and your listeners run as before:

```ts
import 'chartist/styles.css';
import { LineChart } from 'chartist';
import { compat } from 'chartist/compat';

const chart = new LineChart(
  '#chart',
  {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    series: [[12, 9, 7, 8, 5]]
  },
  { plugins: [compat] }
);

chart.on('draw', data => {
  if (data.type === 'point' && data.index === 4) {
    data.element.addClass('is-friday');
  }
});
```

Without `compat`, a listener for a 1.x event isn't called, and the console tells you once per event name what to add. In TypeScript it doesn't compile either: importing `chartist/compat` is what declares the 1.x events, so `data` in a `draw` listener is typed as before.

With `compat`, `draw` events come in the 1.x order and with the 1.x payloads. They fire right after the chart is in the DOM, so `data.element` is a live element you can measure, animate or replace. One small difference: an element you append to `data.group` lands after the group's other elements.

## Your 1.x plugins

A 1.x plugin is a function that gets the chart (and its options). On the classes, those plugins run when `compat` is in the list too:

```ts
import 'chartist/styles.css';
import { LineChart } from 'chartist';
import { compat, type Plugin } from 'chartist/compat';

// A 1.x plugin: a function that gets the chart and its options.
const caption: Plugin = (chart, options) => {
  chart.on('created', () => {
    chart.container.setAttribute('aria-label', options.text);
  });
};

new LineChart(
  '#chart',
  { series: [[1, 3, 2, 4]] },
  { plugins: [compat, [caption, { text: 'Visitors per week' }]] }
);
```

Without `compat`, Chartist skips the plugin and logs a warning that explains how to turn it on.

## Deprecated in 2.x, removed in 3.0

The classes, and everything that exists only for them, are deprecated. They keep working through all of 2.x, and 3.0 removes them from `chartist`. Nothing changes at runtime and nothing is logged: your editor strikes the names through and its hint says what replaces them.

- **`LineChart`, `BarChart`, `PieChart`:** use `lineChart()`, `barChart()` and `pieChart()`, or `chart()` for the rest.
- **`LineChartOptions`, `BarChartOptions`, `PieChartOptions`:** the options of the functions are `LineChartPresetOptions`, `BarChartPresetOptions` and `PieChartPresetOptions`.
- **`AutoScaleAxis`, `FixedScaleAxis`, `StepAxis`:** use `autoScaleAxis()`, `fixedScaleAxis()` and `stepAxis()`.
- **`EventEmitter` and the classes' events types:** the handle a chart function returns has `on()` and `off()`.
- **`DrawEvent`, `CreatedEvent`, `Plugin` and the other 1.x event types:** import the same types from `chartist/compat`.

The data types, `Svg`, `SvgPath`, `easings`, `Interpolation` and the helpers aren't deprecated: the functions use them too.

If your linter treats deprecations as errors (for example `@typescript-eslint/no-deprecated`), this is where it shows up. Move one chart at a time, as the next section shows.

## Moving to the functions

Each class has a function with the same arguments and the same options: `lineChart()`, `barChart()` and `pieChart()`. The functions are where the new features are, and they're the smaller bundle too. You can move one chart at a time; classes and functions work side by side on one page.

### 1. Swap the constructor

```ts nocheck
// 1.x
const chart = new LineChart('#chart', data, options, responsiveOptions);

// v2
const chart = lineChart('#chart', data, options, responsiveOptions);
```

The data, the options and the media query pairs in `responsiveOptions` stay as they are.

### 2. Know what's different about the handle

`lineChart()` returns a small handle instead of a class instance:

| 1.x class                                        | v2 function                                                                      |
| ------------------------------------------------ | -------------------------------------------------------------------------------- |
| First render on the next tick                    | Renders right away, before the call returns                                      |
| Redraws when the window or its container resizes | Redraws when the container resizes (`ResizeObserver`)                            |
| `chart.update(newData)`                          | `chart.update({ data: newData })`, see [Updating charts](https://chartist.dev/docs/updating-charts/)    |
| `chart.update(undefined, options, true)`         | `chart.setOptions(options)`                                                      |
| `chart.update(undefined, options)`               | `chart.setOptions(options, { replace: true })`                                   |
| `chart.detach()`                                 | `chart.detach()`, or `chart.destroy()` to also remove the SVG                    |
| `chart.on('draw', …)` and other 1.x events       | Pipeline events like `compose`, see [Events and plugins](https://chartist.dev/docs/events-and-plugins/) |

### 3. Rewrite draw handlers as plugins

A 1.x `draw` handler changes elements after they're drawn. In v2, a plugin changes the chart's scene before it's rendered: a `compose` listener gets each node and returns a changed copy, or nothing to keep it.

```ts nocheck
// 1.x
chart.on('draw', data => {
  if (data.type === 'point' && data.index === 4) {
    data.element.addClass('is-friday');
  }
});
```

```ts
import 'chartist/styles.css';
import { lineChart, type ChartPlugin } from 'chartist';

// v2
const markFriday: ChartPlugin = {
  name: 'mark-friday',
  install(chart) {
    chart.on('compose', node =>
      node.role === 'point' && node.ctx.valueIndex === 4
        ? node.addClass('is-friday')
        : undefined
    );
  }
};

lineChart(
  '#chart',
  {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    series: [[12, 9, 7, 8, 5]]
  },
  { plugins: [markFriday] }
);
```

Because it works on the scene, this version also runs when you [render on the server](https://chartist.dev/docs/server-rendering/).

### 4. Bring 1.x plugins along with `fromLegacyPlugin`

The functions accept only v2 plugins, and throw if you pass a plain function. Wrap a 1.x plugin with `fromLegacyPlugin()` from `chartist/compat`. It turns on the 1.x events for that chart and calls your plugin with a 1.x-style chart object (`container`, `data`, `options`, `svg`, `on`, `off`, `update`, `detach`):

```ts
import 'chartist/styles.css';
import { lineChart } from 'chartist';
import { fromLegacyPlugin, type Plugin } from 'chartist/compat';

const caption: Plugin = (chart, options) => {
  chart.on('created', () => {
    chart.container.setAttribute('aria-label', options.text);
  });
};

lineChart(
  '#chart',
  { series: [[1, 3, 2, 4]] },
  { plugins: [fromLegacyPlugin(caption, { text: 'Visitors per week' })] }
);
```

You don't need `compat` in the list as well.

> **Register 1.x listeners in a plugin.** The functions render before they
> return, so a `draw` listener added with `chart.on()` afterwards misses the
> first render. Put it in a plugin instead:
> `fromLegacyPlugin(chart => chart.on('draw', …))`. Listeners of the v2 phases
> go in the `on` option: `lineChart('#chart', data, { on: { compose: … } })`.

## Community plugins

Most community plugins, like the tooltips, axis titles and legends on the [plugins page](https://chartist.dev/plugins/), were written for 0.x. They're scripts: they read the global `Chartist`, register on `Chartist.plugins`, and check the chart type with `chart instanceof Chartist.Line`.

Importing `chartist/compat` sets that global up for them, in every build: the library, compat, and the 0.x names they use (`plugins`, `Line`, `Bar` and `Pie` for the chart classes, and `getDataArray`). It only adds what's missing, so plugins that already registered keep their place. These names live on the global only; they aren't exports.

Then load the plugin as a script that runs after that import, and wrap it with `fromLegacyPlugin`. How depends on how you load Chartist.

These plugins need a browser: their `draw` and `created` events fire when the chart is drawn in the page, so they don't run in [server rendering](https://chartist.dev/docs/server-rendering/).

### With modules, without a bundler

The plugin script needs `defer` and has to come after the module that imports compat: deferred scripts and module scripts run in document order. Then your chart code runs after both.

```html
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/index.min.css"
/>
<script type="module">
  import 'https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/compat.min.js';
</script>
<script
  defer
  src="https://cdn.jsdelivr.net/npm/chartist-plugin-axistitle@0.0.7/dist/chartist-plugin-axistitle.js"
></script>

<div id="chart" class="ct-golden-section"></div>
<script type="module">
  import { lineChart } from 'https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/index.min.js';
  import { fromLegacyPlugin } from 'https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/compat.min.js';

  lineChart(
    '#chart',
    { series: [[1, 3, 2, 4]] },
    {
      plugins: [
        fromLegacyPlugin(
          Chartist.plugins.ctAxisTitle({ axisY: { axisTitle: 'Visits' } })
        )
      ]
    }
  );
</script>
```

Import `index.min.js` with `compat.min.js`, or `index.js` with `compat.js`: each pair shares one copy of Chartist.

### With the UMD build

With classic script tags, load the plugin after Chartist's UMD build and `compat.umd.js`, and use everything from the global:

```html
<script src="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/compat.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartist-plugin-axistitle@0.0.7/dist/chartist-plugin-axistitle.js"></script>

<div id="chart" class="ct-golden-section"></div>
<script>
  Chartist.lineChart(
    '#chart',
    { series: [[1, 3, 2, 4]] },
    {
      plugins: [
        Chartist.fromLegacyPlugin(
          Chartist.plugins.ctAxisTitle({ axisY: { axisTitle: 'Visits' } })
        )
      ]
    }
  );
</script>
```

On the 1.x classes, list the plugin next to `compat` as before: `new Chartist.LineChart('#chart', data, { plugins: [Chartist.compat, Chartist.plugins.ctAxisTitle()] })`.

### With a bundler

A bundler can't use these plugins as modules: it takes their CommonJS entry, which reads `require('chartist')` and expects the 0.x names on it. Import compat as usual, then load the plugin as a script at runtime, before the first chart that uses it:

```ts nocheck
import { lineChart } from 'chartist';
import { fromLegacyPlugin } from 'chartist/compat';

function loadScript(src: string) {
  return new Promise<void>((resolve, reject) => {
    const script = Object.assign(document.createElement('script'), { src });
    script.onload = () => resolve();
    script.onerror = () => reject(new Error(`Can't load ${src}`));
    document.head.append(script);
  });
}

await loadScript(
  'https://cdn.jsdelivr.net/npm/chartist-plugin-axistitle@0.0.7/dist/chartist-plugin-axistitle.js'
);

lineChart('#chart', data, {
  plugins: [
    fromLegacyPlugin(
      Chartist.plugins.ctAxisTitle({ axisY: { axisTitle: 'Visits' } })
    )
  ]
});
```

`chartist/compat` is marked as having side effects in its package, so bundlers keep the import even where nothing from it is used. In TypeScript, declare the global the plugins register on:

```ts nocheck
declare const Chartist: {
  plugins: Record<string, (options?: unknown) => import('chartist').Plugin>;
};
```

### Plugins that are ES modules

A plugin written for 1.x as an ES module imports `chartist` itself, like [`@stklcode/chartist-plugin-tooltips`](https://chartist.dev/plugins/stklcode-chartist-plugin-tooltips/). Import it as usual and wrap it with `fromLegacyPlugin`; it needs no global.

### One copy of Chartist

Load Chartist once: as modules, or as the UMD build. If compat is imported as a module while the UMD build of another copy already owns the global `Chartist`, compat leaves that global alone and logs a warning. Two copies would break the plugins' type checks: a chart of one copy isn't an instance of the other copy's `LineChart`.

## What's new

These come with the functions. The classes stay as they were in 1.x, so they don't get them:

- **Composed charts:** bars and lines in one chart, several axes, with `chart()`. See [Composed charts](https://chartist.dev/docs/composed-charts/).
- **Container queries** for responsive options, next to media queries (experimental). See [Responsive sugar topping](https://chartist.dev/docs/responsive/).
- **Server rendering** with `toScene()` and `renderToString()`, and SVG text labels. See [Server rendering](https://chartist.dev/docs/server-rendering/).
- **v2 plugins** that hook into every step of the pipeline. See [Events and plugins](https://chartist.dev/docs/events-and-plugins/).
- **`setOptions()`** changes a chart's options after creation, and the **`on` option** listens from the first render. See [Updating charts](https://chartist.dev/docs/updating-charts/#new-options).
- **Stricter types:** scene nodes narrow by role, event names and payloads are checked, and a misspelled event is a compile error instead of a listener that's never called.

And for every chart, classes included:

- **Custom properties** like `--ct-series-a` and `--ct-line-width` in the default stylesheet, and an opt-in accessible palette. See [Styling with CSS](https://chartist.dev/docs/styling-with-css/).

## Classic script tags

The UMD build is still published for pages that load Chartist with a classic `<script>` tag. It's there for backwards compatibility only: for new pages, use a [`<script type="module">`](https://chartist.dev/docs/getting-started/) instead. The compat plugin has a UMD build that adds itself to the global `Chartist`:

```html
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/index.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/index.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartist@2.0.0-rc.2/dist/compat.umd.js"></script>

<div id="chart" class="ct-golden-section"></div>
<script>
  new Chartist.LineChart(
    '#chart',
    { series: [[1, 3, 2, 4]] },
    { plugins: [Chartist.compat] }
  );
</script>
```

The 0.x file names, `dist/chartist.min.js` and `dist/chartist.min.css`, are copies of the same files, so old links keep loading. The 0.x API (`new Chartist.Line(…)`) went away in 1.0 and doesn't come back. Compat puts `Chartist.Line`, `Bar` and `Pie` on the global only so the type checks of [community plugins](#community-plugins) keep working; in your own code, use the functions or the 1.x classes.

## What changed

Nothing was removed. The changes covered above:

- **1.x events** need `compat` in `plugins`. Without it, listeners aren't called and the console warns.
- **1.x plugins** need `compat` on the classes, or `fromLegacyPlugin()` on the functions. The functions throw for a plain plugin function.
- **`draw` events** fire after the DOM render. Elements appended to `data.group` land after the group's other elements.
- **The compat plugin** covers line, bar and pie charts, not composed charts made with `chart()`. It needs the DOM renderer, so it doesn't work with server rendering.

A few 1.x behaviors were bugs, and v2 fixes them, on the classes too:

- **An axis's own `low` and `high`** win over a bar chart's zero line: `axisY: { low: 10 }` starts the axis at 10. In 1.x, the axis always included zero.
- **`fixedScaleAxis` with `divisor`** puts a tick at `high` too: `divisor: 4` gives five ticks. 1.x left out the last one.
- **The donut ring width** comes from the stylesheet (`--ct-donut-width`, 60px) unless you set `donutWidth`, and in the page the ring is laid out for it. Without the Chartist stylesheet, set `donutWidth`, or the ring gets the browser's 1px stroke. When rendering on the server, set `donutWidth` if your stylesheet changes the width.
- **Donut labels** follow `labelPosition`, as pie labels do. In 1.x they always sat on the ring.
- **`stackMode: 'accumulate-relative'`** fits the axis to the stacks below zero too, so you no longer need a `low` for them.
- **`roundWithPrecision(value, 0)`** and `SvgPath` with `accuracy: 0` round to whole numbers. In 1.x, 0 digits fell back to 8, or didn't round at all. `accuracy: false` turns rounding off.
- **`animationBegin` and `animationEnd`** fire with `compat` when you call `data.element.animate()` in a `draw` listener. In 1.x they never reached your listeners.
- **`off(name)`** without a listener removes only your listeners. In 1.x it also removed those of plugins.
- **A slice that fills the circle**, like a single value, is drawn around the centre. In 1.x it could sit a few pixels off.
- **x values in descending order** draw a line with the default smoothing. 1.x drew nothing.
- **Holes written as `{ x, y: null }`** leave a gap with `Interpolation.step()` and `simple()` too. 1.x wrote `NaN` into the path.
- **A chart-level `low` or `high` that leaves no range**, like a `low` equal to the stacked high, still gives an axis. 1.x ran until the stack overflowed.
- **Labels next to a tick at the axis start** get their step's width. In 1.x, bar labels could land on top of each other.
- **`referenceValue`** works on line charts too, for the default Y axis. In 1.x it only worked for bars.
- **Series 16 to 26** (`ct-series-p` to `ct-series-z`) are coloured, repeating the first 15 colours. 1.x left them blank.
- **Label spans** have no `xmlns` attribute, so a serialized chart parses as SVG.
- **`SvgPath`** splits, joins, parses, scales and translates consistently.
- **An object option over a number** works, like `chartPadding: { top: 20 }` on a pie. 1.x threw.
- **Charts in an iframe or a JSDOM document** use that document's window, and options arrays made there stay arrays.
- **`detach()` during the first render** sticks, and a missing target throws once, not again later.
- **Printed charts** scale to the page instead of being cut off.

In the API reference: [`chartist/compat`](https://chartist.dev/api/compat/) and [the 1.x classes](https://chartist.dev/api/classes/).

Next: [draw a chart with the functions](https://chartist.dev/docs/getting-started/), or [see what Chartist leaves to CSS](https://chartist.dev/docs/why-chartist/).
