Getting started: install, init and lifecycle

Choose between the CDN build and tree-shaken ESM imports, pick canvas or SVG rendering, size the container correctly, and dispose charts so memory does not grow.

Install and import

npm install echarts
# the full build is about 1 MB minified; the tree-shaken path is far smaller
// Option A: everything, one import. Convenient, largest bundle.
import * as echarts from 'echarts';

// Option B: compose the exact chart you need. This is the production path.
import * as echarts from 'echarts/core';
import { LineChart, BarChart } from 'echarts/charts';
import {
  GridComponent, TooltipComponent, LegendComponent,
  DataZoomComponent, TitleComponent, MarkLineComponent
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';

echarts.use([
  LineChart, BarChart,
  GridComponent, TooltipComponent, LegendComponent,
  DataZoomComponent, TitleComponent, MarkLineComponent,
  LabelLayout, UniversalTransition,
  CanvasRenderer
]);

// Option C: CDN for a static page
// <script src="https://cdn.jsdelivr.net/npm/echarts@6/dist/echarts.min.js"></script>
// exposes a global echarts object
Import styleBundle costFits
import * as echarts from 'echarts'EverythingPrototypes and internal tools where size is irrelevant
echarts/core + echarts.use()Only the listed piecesProduction bundles
CDN script tagZero in your bundle, one requestStatic pages and demos
BothWasted bytesNever — pick one
💡
Components must be registered before the first chart is created, not before the first setOption. If a feature renders nothing — a tooltip that never appears, a legend that is missing — an unregistered component is almost always the cause. The tree-shaken build fails silently rather than warning.

init, container sizing and rendering mode

<div id="revenue" style="width: 100%; height: 360px;"></div>
import { init } from 'echarts/core';

const el = document.getElementById('revenue');

// init needs a sized element. A container with height: 0 renders nothing.
const chart = init(el, null, {
  renderer: 'canvas',            // 'canvas' (default) or 'svg'
  devicePixelRatio: window.devicePixelRatio,   // set explicitly for crisp export
  useDirtyRect: true,            // repaint only changed regions: good for dense updates
  width: 'auto',
  height: 'auto'
});

chart.setOption({
  animationDuration: 400,
  grid: { left: 48, right: 24, top: 32, bottom: 40 },
  tooltip: { trigger: 'axis' },
  xAxis: { type: 'category', data: ['Jan', 'Feb', 'Mar', 'Apr'] },
  yAxis: { type: 'value' },
  series: [{ type: 'line', smooth: true, data: [820, 932, 901, 1290] }]
});

// If the container was hidden at init time, size it and warn the chart:
// const observer = new ResizeObserver(() => chart.resize());
// observer.observe(el);
RendererStrengthWeakness
canvasFast with thousands of marks; the defaultRasterised output; text is not selectable
svgCrisp at any zoom; output is inspectable DOMSlower past a few thousand elements; larger memory footprint
canvas with useDirtyRectCheap partial repaintsSlightly more bookkeeping per frame
canvas at a fixed devicePixelRatioPredictable export resolutionBlurry if the ratio is wrong for the display
  • A chart initialised inside a hidden tab or a collapsed accordion has a zero-size container and renders at 0 by 0 until resize() is called.
  • Do not call init twice on the same element. ECharts warns and returns a broken chart; use getInstanceByDom to reuse an existing one.
  • SVG output goes into the DOM, so a page with many SVG charts accumulates thousands of nodes. Canvas keeps one node per chart.
  • The devicePixelRatio option is what makes an exported PNG match a retina screen. Without it the export looks soft on high-DPI displays.

Dispose, reuse and re-init

import { init, getInstanceByDom, getInstanceByDom as getInstance, dispose } from 'echarts/core';

// Safe get-or-create: no duplicate instances on the same element.
function mount(el, option) {
  const existing = getInstanceByDom(el);
  if (existing) {
    existing.setOption(option, { notMerge: false });
    return existing;
  }
  const chart = init(el);
  chart.setOption(option);
  return chart;
}

// Dispose frees the canvas, listeners and internal stores. Without it, a
// single-page app leaks one renderer per navigation.
function unmount(el) {
  const chart = getInstanceByDom(el);
  if (chart) chart.dispose();
}

// Two other clear paths:
// chart.clear()   removes the option and the drawing, keeps the instance usable
// chart.dispose() tears everything down; the instance must not be used again

// A framework-agnostic lifecycle helper
export function createChartController(el) {
  const observer = new ResizeObserver(() => getInstanceByDom(el)?.resize());
  observer.observe(el);

  let chart = null;

  return {
    render(option) {
      chart = mount(el, option);
      return chart;
    },
    resize() { chart?.resize(); },
    destroy() {
      observer.disconnect();
      chart?.dispose();
      chart = null;
    }
  };
}
// Listening for events: register after init and remove on dispose.
chart.on('click', (params) => {
  console.log(params.seriesName, params.name, params.value);
});

// ECharts keeps the handler alive with the instance, so dispose is the cleanup.
// If you must detach without disposing:
const handler = (params) => console.log(params);
chart.on('click', handler);
chart.off('click', handler);

// The 'finished' event fires after a render or animation completes.
// It is the right moment to read the canvas or take a screenshot.
chart.on('finished', () => {
  const url = chart.getDataURL({ pixelRatio: 2, backgroundColor: '#fff' });
  console.log(url.slice(0, 32));
});
SituationCallWhy
New data for the same chartsetOptionMerges; keeps the instance and its state
The chart shape changed completelysetOption(option, { notMerge: true })Drops stale series
Element removed from the DOMdispose()Frees the renderer and listeners
Container resizedresize()Canvas does not reflow by itself
Reuse the same elementgetInstanceByDom then setOptionAvoids a duplicate instance
Reset to a blank chartclear()Keeps the instance alive

FAQ

Why is my chart blank?
Check three things in order: the container has a real width and height, the components used by the option were registered with echarts.use(), and setOption was called after init on an element that exists. A zero-height container is the most common cause.
Should I use SVG or canvas?
Canvas for anything interactive or dense, which is most dashboards. SVG when the output must be crisp at arbitrary zoom, when the chart is small and static, or when you need CSS to reach the chart's elements. You cannot switch renderer on an existing instance — it is an init-time decision.

Option configuration ECharts in React, Vue and Angular

Last refreshed 2026-09-18.