Jesse A. Green / notebook

Apache ECharts User Guide

Generated May 2026 from apache/echarts at c8adaf4

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Apache ECharts is a powerful, interactive charting and data-visualization library for the browser. It produces over 20 chart types — line, bar, pie, scatter, candlestick, radar, sankey, treemap, sunburst, gauge, geo maps, and a fully programmable custom series — backed by a Canvas/SVG render engine that scales to millions of data points. ECharts powers dashboards, business-intelligence platforms, scientific visualizations, and embedded analytics across the modern web.

This guide takes you from your first chart in five minutes to advanced workflows: declarative charts via the option object, dynamic updates, event handling, theming, internationalization, server-side rendering (SSR), responsive layout, and the on-disk file structure that lets you tree-shake unused chart types out of your production bundle.


1. Quick Start

1.1 In sixty seconds

The fastest way to see ECharts working is to copy this HTML file and open it in a browser. No build tools, no npm install, no server.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>ECharts Hello World</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.min.js"></script>
</head>
<body>
  <div id="main" style="width: 600px; height: 400px;"></div>
  <script>
    const myChart = echarts.init(document.getElementById('main'));
    myChart.setOption({
      title: { text: 'ECharts Getting Started Example' },
      tooltip: {},
      xAxis: { data: ['shirt', 'cardigan', 'chiffon', 'pants', 'heels', 'socks'] },
      yAxis: {},
      series: [{
        name: 'sales',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
      }]
    });
  </script>
</body>
</html>
A 600×400 chart appears with six bars labelled shirt through socks,
a title at the top, and a tooltip that appears on hover.

Important

The container <div> must have an explicit width and height before you call echarts.init(). ECharts measures the DOM element to size the canvas. A height of zero produces an invisible chart and a Can't get DOM width or height warning in the console.

1.2 The three lines that draw every chart

Every ECharts chart is the same three steps:

  1. Init — create an instance bound to a DOM element: echarts.init(dom).
  2. setOption — pass a JavaScript object describing what to draw: chart.setOption(option).
  3. Update — call setOption(...) again to mutate the chart, or dispatchAction(...) to trigger interaction.

The option object is the heart of ECharts. Every chart type, axis, legend, tooltip, color, animation, and event handler is configured by adding keys to that object. You will spend most of your time editing option, not calling functions on the chart instance.

Tip

Treat option as data, not code. Store it in JSON, fetch it from an API, generate it with a server-side template, or build it from a TypeScript type. ECharts is declarative — you describe the chart, not the steps to draw it.

1.3 What you get out of the box

ECharts 6.0.0 ships with:

  • 23 series types — line, bar, pie, scatter, radar, map, tree, treemap, sunburst, sankey, gauge, funnel, graph, chord, parallel, candlestick, boxplot, effectScatter, lines, heatmap, pictorialBar, themeRiver, custom.
  • Dual renderers — Canvas (default, fast for many points) and SVG (resolution-independent, better for accessibility and SSR).
  • Seven coordinate systems — Cartesian (grid/xy), polar, geo (maps), single-axis, parallel, calendar, matrix.
  • Rich interaction — tooltips, legends, data zoom (drag/wheel/pinch), brush selection, axis pointers, animation.
  • 53 built-in locales for date formatting and labels.
  • Accessibility via the aria component (live regions, screen-reader descriptions).
  • Server-side rendering producing a hydratable SVG string for static-first frameworks.
  • A typed TypeScript API with EChartsOption typings on every option key.

2. Installation

ECharts can be installed five ways. Pick by deployment target:

Method Use when
npm install echarts Modern app with bundler (Vite, webpack, Rollup, Parcel).
CDN script tag Static HTML, prototypes, CMS plugins, JSFiddle/CodePen.
Local download (dist/echarts.js) Air-gapped or offline environments.
Pre-bundled flavors (echarts/lib/...) Smallest possible bundle via tree-shaking.
SSR runtime Node-side rendering for static sites or email.

2.1 npm / yarn / pnpm

npm

npm install echarts

yarn

yarn add echarts

pnpm

pnpm add echarts
added 2 packages: echarts@6.0.0, zrender@6.0.0

zrender is ECharts' graphics engine and is pulled in automatically as a dependency. The tslib package is also included for TypeScript helper inlining.

Then in your code:

import * as echarts from 'echarts';

const chart = echarts.init(document.getElementById('main'));
chart.setOption({ /* ... */ });

Tip

The default import * as echarts from 'echarts' imports every chart type and component — about 1 MB minified. For production bundles, switch to the tree-shaken imports shown in §2.4.

2.2 CDN

ECharts is published to two major CDNs.

jsDelivr (worldwide)

<script src="https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/echarts.min.js"></script>

unpkg

<script src="https://unpkg.com/echarts@6.0.0/dist/echarts.min.js"></script>

The package's unpkg and jsdelivr fields both point to dist/echarts.min.js, so the bare https://cdn.jsdelivr.net/npm/echarts URL also works.

Using a CDN exposes a global echarts namespace:

<script>
  const chart = echarts.init(document.getElementById('main'));
</script>

Note

The CDN bundle is the echarts.all flavor — every chart type, every component, every renderer. It is the slowest to download but requires zero configuration.

2.3 Standalone download

If you cannot use a CDN or registry, download the prebuilt files directly from the release page on GitHub and host them yourself. The dist/ folder of the release contains:

File Bundle Size class Renderers Charts
echarts.js / echarts.min.js all Largest (~1 MB min) Canvas + SVG All 23
echarts.common.js / .min.js common Medium (~470 KB min) Canvas + SVG Line, Bar, Pie, Scatter
echarts.simple.js / .min.js simple Smallest (~270 KB min) Canvas only Line, Bar, Pie
echarts.esm.mjs / .esm.min.mjs all ES module Largest Canvas + SVG All 23

Tip

If you only need line/bar/pie/scatter and the basic interactions, echarts.common.min.js cuts your payload by half compared to the full bundle.

2.4 Tree-shaken (custom) build

For the smallest bundle, import only what you use through the echarts/core entry. ECharts exposes use() to register chart types, components, and renderers individually.

// Import the core, no charts and renderers included.
import * as echarts from 'echarts/core';

// Import bar charts. All suffixed with Chart or Component.
import { BarChart } from 'echarts/charts';

// Import the title, tooltip, rectangular coordinate, dataset and transform components.
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent
} from 'echarts/components';

// Features like universal transition and label layout.
import { LabelLayout, UniversalTransition } from 'echarts/features';

// Import the Canvas renderer — it is also used by the SVG renderer.
import { CanvasRenderer } from 'echarts/renderers';

// Register the required components.
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
]);

// The chart instance is created the same way as before.
const chart = echarts.init(document.getElementById('main'));
chart.setOption({ /* ... */ });

Important

Each chart type, component, and renderer must be passed to echarts.use() before you call setOption with that type. Forgetting to register a component is the most common source of "the chart renders but feature X is missing" bugs.

The package.json exports map exposes 100+ stable subpath imports for every chart and component, including:

import { LineChart }         from 'echarts/charts';
import { GraphChart }        from 'echarts/charts';
import { LegendComponent }   from 'echarts/components';
import { ToolboxComponent }  from 'echarts/components';
import { CanvasRenderer }    from 'echarts/renderers';
import { SVGRenderer }       from 'echarts/renderers';

The full list is in §6.3 of the Admin Guide.

2.5 TypeScript types

ECharts ships its types alongside the JavaScript. No @types/echarts install is needed.

import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';

const option: EChartsOption = {
  title: { text: 'Typed Example' },
  xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
  yAxis: { type: 'value' },
  series: [{ type: 'bar', data: [120, 200, 150] }]
};

const chart = echarts.init(document.getElementById('main')!);
chart.setOption(option);

For tree-shaken builds, use the per-component option types:

import type {
  BarSeriesOption,
  LineSeriesOption
} from 'echarts/charts';
import type {
  TitleComponentOption,
  TooltipComponentOption,
  GridComponentOption,
  DatasetComponentOption
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';

type ECOption = ComposeOption<
  | BarSeriesOption | LineSeriesOption
  | TitleComponentOption | TooltipComponentOption
  | GridComponentOption  | DatasetComponentOption
>;

const option: ECOption = { /* ... */ };

Tip

ComposeOption<...> produces a precise type that contains only the chart types and components you registered. Wrong option keys become TypeScript errors at compile time.

2.6 Verifying installation

import * as echarts from 'echarts';
console.log(echarts.version);
6.0.0

If this prints the version string, ECharts loaded correctly. If you see echarts is undefined, the script tag is in the wrong order or your bundler is mis-resolving the module.


3. Your First Real Chart

This section walks through building a chart that exercises 80% of what most users need: title, axes, multiple series, tooltip, legend, and responsive resizing.

3.1 The container

ECharts is bound to a DOM element. Its size is read from clientWidth / clientHeight at init time.

<div id="chart" style="width: 100%; height: 400px;"></div>

Warning

Hidden containers (display:none, zero-height parents, collapsed accordions) measure as 0×0 and the chart will not render. Initialize the chart after the container is visible, or call chart.resize() once it becomes visible.

3.2 init() — creating the chart

const chart = echarts.init(dom, theme, opts);
Parameter Type Default Purpose
dom `HTMLElement null` required (unless opts.ssr)
theme `string object null`
opts.renderer `'canvas' 'svg'` 'canvas'
opts.useDirtyRect boolean false Enable dirty-rectangle optimization for canvas.
opts.devicePixelRatio number window.devicePixelRatio Pixel ratio for HiDPI screens.
opts.width `number 'auto'` DOM clientWidth
opts.height `number 'auto'` DOM clientHeight
opts.locale string 'EN' Locale code (see §11).
opts.useCoarsePointer `boolean 'auto'` 'auto'
opts.pointerSize number unset Hover hit-area expansion in pixels.
opts.ssr boolean false Server-side rendering mode (no DOM, returns SVG string).
const chart = echarts.init(document.getElementById('chart'), 'dark', {
  renderer: 'canvas',
  useDirtyRect: true
});

Caution

Calling echarts.init twice on the same DOM element returns the already-existing instance (with a console warning in dev mode). Always call chart.dispose() before re-initializing.

3.3 setOption — describing the chart

setOption accepts the entire chart configuration as one object. ECharts merges it into any previous option (additive update). To replace the option completely, pass notMerge: true or use the replaceMerge field.

chart.setOption({
  title: {
    text: 'Monthly Revenue',
    subtext: 'FY2025',
    left: 'center'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'shadow' }
  },
  legend: {
    bottom: 0,
    data: ['Revenue', 'Costs']
  },
  grid: {
    left: '4%',
    right: '4%',
    bottom: '12%',
    containLabel: true
  },
  xAxis: {
    type: 'category',
    data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
  },
  yAxis: {
    type: 'value',
    axisLabel: { formatter: '${value}K' }
  },
  series: [
    {
      name: 'Revenue',
      type: 'bar',
      data: [120, 200, 150, 80, 70, 110]
    },
    {
      name: 'Costs',
      type: 'line',
      data: [80, 110, 90, 60, 55, 75]
    }
  ]
});
Renders a combined bar/line chart with title, legend at bottom,
axis-trigger tooltip, dollar-formatted Y axis, and 6 months of data.

3.4 setOption signatures

ECharts accepts two call shapes:

// Legacy positional API
chart.setOption(option, notMerge, lazyUpdate);

// Modern options-object API (recommended)
chart.setOption(option, {
  notMerge: false,        // Replace option entirely instead of merging
  replaceMerge: ['series'], // Replace only listed mainTypes
  lazyUpdate: false,      // Defer redraw until next animation frame
  silent: false,          // Skip events fired by this update
  transition: undefined,  // Custom inter-state transitions
  darkMode: undefined     // Force light/dark mode resolution
});
Field Default Effect
notMerge false When true, discards previous option and uses only the new one.
replaceMerge unset Array of mainTypes (e.g. ['series', 'yAxis']) to replace instead of merge. Other types still merge.
lazyUpdate false Useful when calling setOption very frequently — coalesces redraws to the next frame.
silent false Suppress events fired as a result of this update.

Important

Repeatedly calling setOption with notMerge: false (the default) adds rather than replaces. If the new option has fewer series than the old one, the leftover series stay on the chart. Use replaceMerge: ['series'] or notMerge: true for full replacement.

3.5 Resizing

ECharts does not auto-resize. You must call chart.resize() whenever the container changes size (window resize, sidebar collapse, tab activation).

window.addEventListener('resize', () => chart.resize());

For modern apps, ResizeObserver is more precise:

const ro = new ResizeObserver(() => chart.resize());
ro.observe(document.getElementById('chart'));

You can also pass an explicit size to override the container measurement:

chart.resize({ width: 800, height: 600 });
chart.resize({ width: 'auto', height: 'auto' });   // Re-measure DOM
chart.resize({ silent: true });                     // Don't fire events
chart.resize({ animation: { duration: 300, easing: 'cubicOut' } });

3.6 Cleanup

When you remove a chart from the DOM (component unmount, route change), call dispose() to release resources:

chart.dispose();

Or dispose by DOM reference:

echarts.dispose(document.getElementById('chart'));
echarts.dispose(chart);          // Pass the instance
echarts.dispose(chart.id);       // Or its string ID

Warning

Forgetting dispose() on chart removal leaks DOM listeners and zrender resources, which over time degrades performance and can cause memory pressure in single-page apps that mount/unmount many charts.


4. Chart Types

ECharts has 23 built-in series types. This section shows the option shape for each. For an interactive gallery, see echarts.apache.org/examples.

4.1 Cartesian basics

Line

{
  xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] },
  yAxis: { type: 'value' },
  series: [{
    type: 'line',
    data: [120, 132, 101, 134, 90],
    smooth: true,         // Curved line
    areaStyle: {},        // Fill below
    showSymbol: true,     // Dots at data points
    symbol: 'circle',
    symbolSize: 8
  }]
}

Bar

{
  xAxis: { type: 'category', data: ['A', 'B', 'C', 'D'] },
  yAxis: { type: 'value' },
  series: [{
    type: 'bar',
    data: [10, 22, 28, 43],
    barWidth: '60%',
    itemStyle: {
      borderRadius: [4, 4, 0, 0]
    }
  }]
}

Stacked bar

Set stack to a shared string on multiple bar series:

series: [
  { type: 'bar', stack: 'total', data: [10, 22, 28, 43], name: 'A' },
  { type: 'bar', stack: 'total', data: [5, 8, 12, 15],  name: 'B' }
]

Scatter / EffectScatter

{
  xAxis: {}, yAxis: {},
  series: [{
    type: 'scatter',
    data: [[10.0, 8.04], [8.0, 6.95], [13.0, 7.58], [9.0, 8.81]],
    symbolSize: 20
  }]
}

effectScatter adds a rippling animation around each point and is useful for highlighting key data points (e.g. capital cities on a map).

4.2 Statistical

Boxplot

{
  xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
  yAxis: { type: 'value' },
  series: [{
    type: 'boxplot',
    data: [
      [low, q1, median, q3, high],   // Q1
      [low, q1, median, q3, high]    // Q2
      // ...
    ]
  }]
}

The prepareBoxplotData helper in the optional dataTool extension computes the five-number summary for you. See §13.

Candlestick

For OHLC financial data:

{
  xAxis: { type: 'category', data: dates },
  yAxis: {},
  series: [{
    type: 'candlestick',
    data: [
      [open, close, low, high],  // Day 1
      // ...
    ]
  }]
}

Heatmap

series: [{
  type: 'heatmap',
  data: [[xIndex, yIndex, value], ...]
}]

Often paired with a visualMap component to map value → color.

4.3 Hierarchical

Pie

{
  series: [{
    type: 'pie',
    radius: ['40%', '70%'],   // Inner/outer = donut
    data: [
      { value: 1048, name: 'Search' },
      { value: 735, name: 'Direct' },
      { value: 580, name: 'Email' }
    ]
  }]
}

Sunburst

A multi-level pie:

series: [{
  type: 'sunburst',
  data: [{
    name: 'Root',
    children: [
      { name: 'A', value: 10, children: [/* ... */] },
      { name: 'B', value: 30 }
    ]
  }],
  radius: [0, '90%']
}]

Treemap

series: [{
  type: 'treemap',
  data: [{
    name: 'NodeA',
    value: 100,
    children: [{ name: 'Sub1', value: 60 }, { name: 'Sub2', value: 40 }]
  }],
  leafDepth: 1     // Show only this depth, drill on click
}]

Tree

series: [{
  type: 'tree',
  data: [{
    name: 'Root',
    children: [
      { name: 'A', children: [/* ... */] }
    ]
  }],
  layout: 'orthogonal',   // 'orthogonal' | 'radial'
  orient: 'LR'            // Left→Right
}]

4.4 Relational

Graph (network / force-directed)

series: [{
  type: 'graph',
  layout: 'force',                  // 'none' | 'circular' | 'force'
  data: [
    { name: 'n1', x: 100, y: 100, value: 5 },
    { name: 'n2', x: 200, y: 200, value: 8 }
  ],
  links: [
    { source: 'n1', target: 'n2' }
  ],
  force: { repulsion: 100 },
  roam: true
}]

Sankey

series: [{
  type: 'sankey',
  data: [
    { name: 'A' }, { name: 'B' }, { name: 'C' }
  ],
  links: [
    { source: 'A', target: 'B', value: 5 },
    { source: 'B', target: 'C', value: 3 }
  ]
}]

Chord

A circular relationship diagram. New in v6 as a standalone series (formerly only via graph with circular layout).

series: [{
  type: 'chord',
  data: [{ name: 'A' }, { name: 'B' }, { name: 'C' }],
  edges: [
    { source: 'A', target: 'B', value: 10 },
    { source: 'B', target: 'C', value: 15 }
  ]
}]

4.5 Geographic

Map

echarts.registerMap('USA', usaGeoJson);

{
  series: [{
    type: 'map',
    map: 'USA',
    data: [
      { name: 'California', value: 100 },
      { name: 'Texas', value: 70 }
    ]
  }]
}

ECharts does not bundle map definitions. Register a GeoJSON or SVG asset with echarts.registerMap(name, geoJson) first. See §10.

Lines

Animated routes between two points (great for migration / flight data):

series: [{
  type: 'lines',
  coordinateSystem: 'geo',
  data: [
    { coords: [[fromLng, fromLat], [toLng, toLat]] }
  ],
  lineStyle: { width: 1, opacity: 0.6, curveness: 0.2 },
  effect: { show: true, period: 6, trailLength: 0.5 }
}]

4.6 Specialized

Series Use case
radar Multi-axis comparison (skills profile, product spec sheet).
gauge Single-value progress dial.
funnel Conversion stages (form fills → sign-ups → activations).
parallel High-dimension data, parallel coordinate plot.
themeRiver Stacked time series rendered as a flowing river.
pictorialBar Bars rendered as repeated icon shapes.
custom Programmable rendering for any chart not above.

Each series has the same call shape: pass an item to series: [...] with type and series-specific keys.

4.7 The custom series escape hatch

When no built-in series fits, custom lets you draw any chart by returning render-item descriptors:

series: [{
  type: 'custom',
  renderItem(params, api) {
    const xValue = api.value(0);
    const yValue = api.value(1);
    const point = api.coord([xValue, yValue]);
    return {
      type: 'rect',
      shape: { x: point[0] - 10, y: point[1] - 10, width: 20, height: 20 },
      style: { fill: api.visual('color') }
    };
  },
  data: [[1, 5], [2, 8], [3, 3]]
}]

This single mechanism powers most third-party chart extensions for ECharts — radial bar charts, gantt charts, hexbin maps, etc.


5. Working with Data

5.1 Direct series.data

The simplest data model: each series owns its array.

series: [{
  type: 'bar',
  data: [120, 200, 150, 80]
}]

Items can be primitives, arrays, or objects:

data: [
  120,                                    // value
  [10, 5],                                // [x, y]
  { value: 100, name: 'Highlight', itemStyle: { color: '#f88' } }
]

5.2 The dataset component

For multi-series charts that share data, register the data once with the dataset component:

{
  dataset: {
    source: [
      ['product',  '2022', '2023', '2024'],
      ['Matcha',     43.3,    85.8,    93.7],
      ['Milk Tea',   83.1,    73.4,    55.1],
      ['Cheese Tea', 86.4,    65.2,    82.5]
    ]
  },
  xAxis: { type: 'category' },
  yAxis: {},
  series: [
    { type: 'bar' },   // Auto-uses dataset
    { type: 'bar' },
    { type: 'bar' }
  ]
}

The first row is treated as headers; subsequent rows become a series each. Use seriesLayoutBy: 'row' (default) or 'column' to flip orientation.

Tip

Datasets are the right choice when the same data drives multiple visuals (e.g. switching between bar and line representations of the same numbers). They also enable the powerful transform feature below.

5.3 Transforms (filter, sort, aggregate)

Datasets can be derived from other datasets via the transform component:

{
  dataset: [
    {
      source: [/* original data */]
    },
    {
      transform: { type: 'filter', config: { dimension: 'Year', '>=': 2023 } }
    },
    {
      fromDatasetIndex: 1,
      transform: { type: 'sort', config: { dimension: 'Value', order: 'desc' } }
    }
  ],
  series: [{ type: 'bar', datasetIndex: 2 }]
}

Built-in transforms include filter and sort. Register custom transforms via echarts.registerTransform(transform).

5.4 Streaming with appendData

For real-time data (live ticks, IoT streams) that grows continuously, use appendData instead of setOption to avoid full-chart redraws:

chart.appendData({
  seriesIndex: 0,
  data: [[Date.now(), 42.7]]
});

This appends rows in-place. The chart redraws only the new region.

5.5 universalTransition

Cross-chart morphing animations: when you switch from a bar chart to a pie chart with the same data IDs, ECharts can morph each shape across the boundary.

series: [{
  type: 'bar',
  data: [...],
  universalTransition: { enabled: true }
}]

The UniversalTransition feature must be registered (use([UniversalTransition])) — it is included in the all build by default.


6. Interaction

6.1 Tooltip

tooltip: {
  trigger: 'axis',                   // 'axis' | 'item' | 'none'
  axisPointer: { type: 'shadow' },   // 'line' | 'shadow' | 'cross' | 'none'
  formatter: (params) => {
    return params.map(p => `${p.seriesName}: ${p.value}`).join('<br/>');
  },
  backgroundColor: 'rgba(50,50,50,0.9)',
  textStyle: { color: '#fff' },
  confine: true                       // Keep inside chart bounds
}
trigger When tooltip fires
'axis' Hovering over an axis category — shows all series at that index.
'item' Hovering over a single data point — shows that point only.
'none' Tooltip is registered but not auto-shown (use dispatchAction({type:'showTip'})).

6.2 Legend

legend: {
  data: ['Series 1', 'Series 2'],   // Optional, defaults to series names
  type: 'scroll',                    // Enable scrolling for many series
  orient: 'horizontal',              // 'horizontal' | 'vertical'
  bottom: 0,
  selected: {                        // Initial visibility
    'Series 1': true,
    'Series 2': false
  }
}

Click a legend entry to toggle the corresponding series. Listen for 'legendselectchanged' events to react.

6.3 Data zoom

Adds drag/wheel/pinch zoom to one or more axes.

dataZoom: [
  { type: 'inside', xAxisIndex: 0 },                       // Wheel/pinch on chart
  { type: 'slider', xAxisIndex: 0, height: 24, bottom: 0 } // Bottom slider bar
]
  • inside — zoom by mouse wheel and touch pinch on the chart body.
  • slider — render a draggable scrollbar.

To programmatically zoom:

chart.dispatchAction({
  type: 'dataZoom',
  start: 20,    // Percentage
  end: 80
});

6.4 Brush (range selection)

toolbox: {
  feature: { brush: { type: ['rect', 'polygon', 'clear'] } }
},
brush: {
  toolbox: ['rect', 'polygon', 'keep', 'clear'],
  xAxisIndex: 0
}

Listen for 'brushSelected' to read selected indices.

6.5 Axis pointer

A crosshair-style indicator that follows the mouse:

axisPointer: {
  type: 'cross',          // 'line' | 'cross' | 'shadow' | 'none'
  link: [{ xAxisIndex: 'all' }],   // Sync across multiple charts
  snap: true,
  label: { backgroundColor: '#6a7985' }
}

6.6 Events

chart.on('click', (params) => {
  console.log('Clicked:', params.seriesName, params.data);
});

chart.on('mouseover', { seriesName: 'Revenue' }, (params) => {
  // Filtered: fires only for the Revenue series.
});

chart.off('click');

Common events:

Event Fired when
'click', 'dblclick' User clicks/double-clicks a series item or background.
'mouseover', 'mouseout' Pointer enters/leaves a data item.
'globalout' Pointer leaves the entire chart.
'rendered' A render frame finishes. params.elapsedTime is render duration in ms.
'finished' Animation has finished and chart is at rest.
'legendselectchanged' A legend toggle occurred.
'datazoom' A data-zoom range changed.
'brushselected' A brush selection committed.
'restore' Toolbox restore button pressed.
'dataviewchanged' DataView toolbox edited and applied.
'magictypechanged' Chart type switched via toolbox magicType.
'georoam' A geo map was panned/zoomed.
'timelinechanged', 'timelineplaychanged' Timeline component updates.

You can also listen to raw zrender events (low-level pointer events) via chart.getZr().on(...).

6.7 dispatchAction — programmatic interaction

Anything a user can do, you can do programmatically:

// Highlight a data point
chart.dispatchAction({
  type: 'highlight',
  seriesIndex: 0,
  dataIndex: 3
});

// Show a tooltip
chart.dispatchAction({
  type: 'showTip',
  seriesIndex: 0,
  dataIndex: 5
});

// Zoom
chart.dispatchAction({ type: 'dataZoom', start: 0, end: 50 });

// Toggle a legend entry
chart.dispatchAction({ type: 'legendToggleSelect', name: 'Revenue' });

Tip

dispatchAction is the ideal way to drive a chart from external UI (a button bar that filters, a slider that changes the time range, a map that spotlights a country).


7. Coordinate Systems

A coordinate system places series in 2-D space. Most users only ever touch grid, but ECharts ships seven.

System Series typically using it Configured via
cartesian2d (a.k.a. grid) line, bar, scatter, candlestick, boxplot, heatmap xAxis, yAxis, grid
polar radar, custom radial layouts polar, angleAxis, radiusAxis
geo map, lines, scatter on a map geo
singleAxis themeRiver, scatter on a single axis singleAxis
parallel parallel parallel, parallelAxis
calendar scatter / heatmap on a calendar grid calendar
matrix (new in v6) grids of related charts matrix
{
  grid: { left: '10%', right: '5%', top: 60, bottom: 50 },
  xAxis: { type: 'value' },
  yAxis: { type: 'category' },
  series: [{ type: 'bar', data: [...] }]
}

7.1 Axis types

xAxis: {
  type: 'category' | 'value' | 'time' | 'log',
  data: [...],         // Required for 'category'
  min: 0,
  max: 100,
  boundaryGap: true,
  axisLabel: {
    formatter: '{value}%',
    rotate: 45
  },
  axisLine: { show: true },
  splitLine: { show: false }
}

7.2 Axis breaks

New in v6: insert visual gaps in an axis to skip empty ranges or weekends.

xAxis: {
  type: 'time',
  breaks: [{ start: '2024-12-25', end: '2024-12-26' }]
}

You must register the AxisBreak feature (included in all and common builds).

7.3 The matrix coordinate system (v6 new)

The matrix coord system arranges sub-charts in a grid layout — useful for small-multiples and faceting:

{
  matrix: {
    columns: { data: ['2023', '2024', '2025'] },
    rows:    { data: ['North', 'South'] }
  },
  series: [{
    type: 'pie',
    coordinateSystem: 'matrix',
    coordinateSystemUsage: 'box',
    data: [/* ... */]
  }]
}

8. Theming and Styling

8.1 Built-in themes

ECharts ships 37 themes under theme/*.js. Load and register one:

import 'echarts/theme/dark.js';   // For some prebuilt scripts; or:

// Manual registration
import darkTheme from './theme/dark.json' with { type: 'json' };
echarts.registerTheme('dark', darkTheme);

const chart = echarts.init(dom, 'dark');

The 37 names include: dark, infographic, macarons, roma, shine, vintage, walden, westeros, plus enterprise styles like chalk and wonderland.

8.2 Custom themes

A theme is a plain object that supplies defaults for any option key:

echarts.registerTheme('cyber-panda', {
  color: ['#22d3ee', '#a78bfa', '#f472b6', '#facc15'],
  backgroundColor: '#0f172a',
  textStyle: { color: '#e2e8f0' },
  title: { textStyle: { color: '#f8fafc' } },
  legend: { textStyle: { color: '#cbd5e1' } },
  tooltip: { backgroundColor: 'rgba(30, 41, 59, 0.92)' }
});

Once registered, pass the name to echarts.init(dom, 'cyber-panda').

Tip

The bundled theme/tool/build.js script in the repo can convert exported themes from the official Theme Builder into the registration object format.

8.3 Per-series styling

Theme defaults are starting points. Anything in option overrides the theme. Set colors with:

series: [{
  type: 'bar',
  itemStyle: { color: '#3b82f6' },
  emphasis: { itemStyle: { color: '#1d4ed8' } },
  data: [...]
}]

Or use a function to color by data:

itemStyle: {
  color: (params) => params.value > 100 ? '#ef4444' : '#10b981'
}

8.4 Dark mode

chart.setOption({ darkMode: true, /* ... */ });

Setting darkMode: true re-resolves color choices that depend on background luminance. Combined with backgroundColor: 'transparent' it lets your theme system control the surface color.


9. Animation

ECharts animates everything by default: initial render, data updates, axis changes, hover states.

{
  animation: true,
  animationDuration: 1000,
  animationEasing: 'cubicOut',
  animationDelay: (idx) => idx * 50,
  animationDurationUpdate: 500,
  animationEasingUpdate: 'cubicInOut',
  animationDelayUpdate: 0,
  animationThreshold: 2000   // Disable animation above N data points
}

To disable globally:

{ animation: false }

9.1 Universal transition

Already covered in §5.5. Set universalTransition: { enabled: true } per series for cross-type morphing.

9.2 Label layout

The labelLayout feature prevents label overlap automatically:

series: [{
  type: 'pie',
  labelLayout: { hideOverlap: true }
}]

Or globally:

{ labelLayout: { hideOverlap: true } }

10. Maps (Geo)

ECharts does not bundle map data. You must register a GeoJSON file with registerMap.

10.1 Registering

import worldGeo from './world.geo.json';
echarts.registerMap('world', worldGeo);

The mapName is the key by which the geo component and map series refer to it.

10.2 Using a map

{
  geo: { map: 'world', roam: true },
  series: [{
    type: 'scatter',
    coordinateSystem: 'geo',
    data: [{ name: 'Paris', value: [2.35, 48.85, 100] }]
  }]
}

Or as a series itself:

series: [{
  type: 'map',
  map: 'world',
  data: [
    { name: 'United States', value: 100 },
    { name: 'Brazil', value: 70 }
  ]
}]

10.3 SVG basemaps

registerMap also accepts SVG strings, allowing you to use floor plans, organ diagrams, or any custom shape as a coordinate system:

echarts.registerMap('floor', { svg: svgString });

10.4 Baidu Maps extension (bmap)

For tile-based geographic maps within China, the optional extension-src/bmap extension wraps Baidu Maps as an ECharts coordinate system. See §13.

Warning

Loading extension/bmap requires the Baidu Maps API to be already loaded on the page (<script src="https://api.map.baidu.com/...">). The extension throws "BMap api is not loaded" otherwise. Only one bmap component per chart is allowed.


11. Internationalization

ECharts ships 53 locale packs under i18n/. Each defines toolbox tooltips, datazoom labels, time formatter strings, etc.

import 'echarts/i18n/langDE.js';

const chart = echarts.init(dom, null, { locale: 'DE' });

Or register from JSON:

import jaLocale from 'echarts/i18n/langJA-obj.json';
echarts.registerLocale('JA', jaLocale);

Locale codes shipped (subset shown — full list in i18n folder):

Code Language
EN English (default)
ZH Chinese (Simplified)
ZH-TW Chinese (Traditional)
JA Japanese
KO Korean
DE German
FR French
ES Spanish
PT-BR Portuguese (Brazil)
IT Italian
RU Russian
AR Arabic
HE Hebrew

Locale strings affect: toolbox button tooltips, dataZoom slider labels, chart-level "loading" text, and built-in time axis date formatting.


12. Server-Side Rendering (SSR)

ECharts can render to an SVG string in Node.js, returning a static markup payload that a browser can later hydrate to add interactivity.

12.1 Server side — produce SVG

import * as echarts from 'echarts';

const chart = echarts.init(null, null, {
  renderer: 'svg',
  ssr: true,
  width: 600,
  height: 400
});

chart.setOption({
  title: { text: 'Sales' },
  xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
  yAxis: {},
  series: [{ type: 'bar', data: [10, 20, 30] }]
});

const svg = chart.renderToSVGString();
// Send `svg` in your HTML response.
chart.dispose();

12.2 Client side — hydrate

The static SVG is markup. Add interactivity (hover, click) without re-rendering by importing the lightweight SSR client:

import { hydrate } from 'echarts/ssr/client';

hydrate(document.getElementById('chart'), {
  on: {
    mouseover: ({ seriesIndex, dataIndex }) => { /* ... */ },
    mouseout:  ({ seriesIndex, dataIndex }) => { /* ... */ },
    click:     ({ seriesIndex, dataIndex }) => { /* ... */ }
  }
});

The hydrate runtime is ~3 KB minified — much smaller than the full ECharts library — making it ideal for static-first frameworks (Astro, Next.js RSC, 11ty) where you want a fast first paint and only ship interaction code on demand.

Important

SSR hydration is event-only: you can't setOption or update data after hydration without loading full ECharts. For dynamic charts, render a placeholder server-side and bootstrap full ECharts on the client.


13. Optional Extensions

13.1 dataTool (echarts/extension/dataTool)

Helpers for transforming raw data:

import echarts from 'echarts';
import 'echarts/extension/dataTool';

// Boxplot summary
const stats = echarts.dataTool.prepareBoxplotData(rawArrays);
// stats.boxData = [[low, q1, median, q3, high], ...]
// stats.outliers = [[index, value], ...]

// GEXF (Gephi format) parser for graphs
const graphData = echarts.dataTool.gexf.parse(gexfXmlString);

13.2 bmap (echarts/extension/bmap)

Baidu Maps coordinate system for tile-based Chinese geographic visualization:

<script src="https://api.map.baidu.com/api?v=3.0&ak=YOUR_KEY"></script>
<script src="echarts.min.js"></script>
<script src="extension/bmap.min.js"></script>
{
  bmap: {
    center: [116.397, 39.90],
    zoom: 10,
    roam: true,
    mapStyle: { /* Baidu mapStyle config */ }
  },
  series: [{
    type: 'scatter',
    coordinateSystem: 'bmap',
    data: [/* [lng, lat, value] */]
  }]
}

14. Accessibility

The aria component generates ARIA labels and screen-reader descriptions:

{
  aria: {
    enabled: true,
    description: 'Quarterly revenue chart for FY2025',
    decal: { show: true }    // Add patterns for color-blind users
  }
}

decal.show: true overlays distinct visual patterns (lines, dots, crosshatches) on each series so users who cannot distinguish colors can still tell series apart.


15. Responsive & Media Queries

ECharts supports option-level media queries — different options for different chart sizes:

chart.setOption({
  baseOption: {
    title: { text: 'Sales' },
    series: [{ type: 'bar', data: [10, 20, 30] }]
  },
  media: [
    {
      query: { maxWidth: 600 },
      option: {
        legend: { orient: 'vertical', right: 0 }
      }
    },
    {
      query: { minWidth: 1200 },
      option: {
        title: { textStyle: { fontSize: 24 } }
      }
    }
  ]
});

When the chart is resized (via resize()) ECharts re-evaluates queries and merges the matching option block.


16. Common Tasks (Recipes)

16.1 Save the chart as an image

const url = chart.getDataURL({
  type: 'png',                 // 'png' | 'jpeg' | 'svg'
  pixelRatio: 2,               // Higher → sharper
  backgroundColor: '#fff',
  excludeComponents: ['toolbox']
});

const link = document.createElement('a');
link.download = 'chart.png';
link.href = url;
link.click();

16.2 Combine multiple charts into one image

const url = echarts.connect([chart1, chart2]) && chart1.getConnectedDataURL({
  type: 'png',
  pixelRatio: 2,
  backgroundColor: '#fff'
});

16.3 Sync zoom across multiple charts

echarts.connect([chart1, chart2, chart3]);
// or
chart1.group = 'shared';
chart2.group = 'shared';
chart3.group = 'shared';
echarts.connect('shared');

After connecting, dataZoom, legend selection, tooltip axisPointer, and brushing propagate across the group.

16.4 Export rendered SVG as a string

const svg = chart.renderToSVGString();   // Requires renderer: 'svg'

16.5 Render to a fresh canvas (snapshot)

const canvas = chart.renderToCanvas({ pixelRatio: 2, backgroundColor: '#fff' });
document.body.appendChild(canvas);

Caution

renderToCanvas only works when the chart was initialized with renderer: 'canvas'. Otherwise it throws "renderToCanvas can only be used in the canvas renderer." Same for renderToSVGString and SVG.

16.6 Convert pixel ↔ data coordinates

const pixelXY = chart.convertToPixel({ seriesIndex: 0 }, [dataX, dataY]);
const dataXY  = chart.convertFromPixel({ seriesIndex: 0 }, [pixelX, pixelY]);
const inside  = chart.containPixel({ seriesIndex: 0 }, [pixelX, pixelY]);

Useful for synchronising overlay HTML or canvas content with the chart.

16.7 Loading state

chart.showLoading();              // 'default' loading effect
chart.showLoading('default', { text: 'Fetching data...' });
chart.hideLoading();

Register a custom loading effect:

echarts.registerLoading('spinner', (api, cfg) => { /* return a zrender Group */ });
chart.showLoading('spinner');

16.8 Get / inspect the current option

const opt = chart.getOption();
console.log(opt.series.length, 'series active');

16.9 Replace a single mainType cleanly

chart.setOption({ series: [{ type: 'bar', data: newData }] }, {
  replaceMerge: ['series']
});

This is the safest way to swap a chart's series list without manually clearing it.

16.10 Clear the chart

chart.clear();

Removes all series and components but keeps the instance alive for reuse.


17. Troubleshooting

Error: Initialize failed: invalid dom.

Cause: echarts.init() was called with null, undefined, or a non-existent element ID, and opts.ssr was not set.

Solution: Make sure document.getElementById('main') returns a non-null element before init. If you load ECharts in <head>, defer init to DOMContentLoaded or move the script to the bottom of <body>.

Prevention: Always assert the dom is present first:

const dom = document.getElementById('main');
if (!dom) throw new Error('Chart container missing');
const chart = echarts.init(dom);

Error: renderToCanvas can only be used in the canvas renderer.

Cause: The chart was initialized with renderer: 'svg' and chart.renderToCanvas() was called.

Solution: Either init with renderer: 'canvas', or call chart.renderToSVGString() instead.

Prevention: Check chart.getZr().painter type or store the renderer choice alongside the chart.

Error: renderToSVGString can only be used in the svg renderer.

Cause: Mirror of the previous error: chart is canvas, you asked for SVG.

Solution: Init with renderer: 'svg' (this is also required for SSR).

Error: Option should contains series.

Cause: setOption was called with no series key (or an empty array) and the chart cannot determine what to draw.

Solution: Provide at least one entry in series: [...]. To clear the chart, use chart.clear() instead.

Error: Illegal priority

Cause: Used echarts.registerProcessor / registerVisual etc. with a numeric priority outside the allowed buckets.

Solution: Pass one of the predefined constants from echarts.PRIORITY (e.g. PRIORITY.PROCESSOR.SERIES_FILTER) or omit priority to use the default.

Error: Unknown dimension

Cause: A series referenced a dimension index or name that does not exist in the data store.

Solution: Verify series.encode keys match dimension names defined in your dataset.dimensions or the column count of dataset.source.

Error: BMap api is not loaded

Cause: The extension/bmap script ran before the Baidu Maps SDK was on the page.

Solution: Ensure the order is: Baidu Maps <script> first, then echarts.min.js, then extension/bmap.min.js. All three must be loaded before setOption.

Error: Only one bmap component can exist

Cause: The bmap extension supports only a single bmap component per chart instance.

Solution: If you need multiple maps, create multiple chart instances.

Error: render method must been implemented

Cause: A custom ChartView subclass was registered without overriding render().

Solution: Implement render(seriesModel, ecModel, api, payload) in your subclass. See the Admin Guide §11 for chart extension internals.

Warning: There is a chart instance already initialized on the dom.

Cause: echarts.init was called twice on the same DOM element. The second call returned the existing instance.

Solution: Call chart.dispose() before re-initializing, or check echarts.getInstanceByDom(dom) first.

Warning: Can't get DOM width or height. ... should not be 0.

Cause: Container has zero size at init time (often inside a hidden tab, accordion, or modal).

Solution: Init only after the container is visible, or pass opts.width and opts.height explicitly. For tabs, listen for the show event and call chart.resize().

Symptom: Chart is blank / nothing rendered

Likely causes:

  1. Container has zero height (CSS fix: height: 400px).
  2. A required component or chart type was not registered (echarts.use(...)) in a tree-shaken build.
  3. series is empty or data is null.
  4. The chart was disposed (chart.isDisposed() === true).

Diagnosis:

console.log('disposed?', chart.isDisposed());
console.log('size:', chart.getWidth(), chart.getHeight());
console.log('opt:', JSON.stringify(chart.getOption().series));

Symptom: Tooltip shows but is in the wrong position / clipped

Cause: Tooltip is positioned in the document body (default appendToBody: true) but the chart is inside a transformed parent (CSS transform, filter, or position: fixed).

Solution: Set tooltip.appendToBody: false to position relative to the chart container, or tooltip.confine: true to clip inside the chart bounds.

Symptom: setOption frequently and animations stutter

Cause: Each setOption triggers a full update cycle. Frequent calls (>10/sec) overwhelm the render loop.

Solution: Use lazyUpdate: true in setOption opts to coalesce updates, or appendData() for streaming inserts. Also consider animation: false for high-frequency feeds.

Symptom: Memory grows after repeatedly mounting/unmounting charts

Cause: Missing chart.dispose() on unmount.

Solution: Call chart.dispose() in the unmount/cleanup hook of your component:

// React
useEffect(() => {
  const chart = echarts.init(ref.current);
  return () => chart.dispose();
}, []);
// Vue 3
onBeforeUnmount(() => chart.dispose());

Symptom: Time axis displays wrong dates with bar series

Cause: Known issue with xAxis.type: 'time' combined with series.type: 'bar' (community issue #19737).

Workaround: Use xAxis.type: 'category' with pre-formatted date strings, or pass exact timestamp numbers as bar data x-values.

Symptom: Tooltip not displayed when symbol is 'none' and labels hidden

Cause: Reported in community issue #17202. Hit-test region is inferred from symbols/labels.

Workaround: Set tooltip.trigger: 'axis' (which uses an axis-wide hit area) instead of trigger: 'item'.

Symptom: Memory leak with LTTB sampling on overlapping points

Cause: Reported in community issue #18562 for series.line with sampling: 'lttb' and >10K overlapping points.

Workaround: Disable sampling (sampling: 'none') or pre-deduplicate overlapping x-values before passing to ECharts.


18. Frequently Asked Questions

Q: Is ECharts free to use commercially?

Yes. Apache ECharts is licensed under the Apache License 2.0, which permits commercial use, modification, distribution, patent grant, and private use. No royalty or attribution beyond the license header is required.

Q: Does ECharts work in Node.js?

Yes, in SSR mode (opts.ssr: true, renderer: 'svg'). The output is an SVG string. See §12. You will need a JSDOM-free build path; ECharts itself does not require browser globals in SSR mode but check that any third-party feature you use does not.

Q: Does ECharts work in React Native / mobile WebView?

In a WebView yes (it's just a web page). For native rendering, projects such as react-native-echarts-pro wrap ECharts in a WebView under the hood. There is no native React Native renderer maintained by the ECharts team.

Q: Can I use ECharts with React / Vue / Svelte / Angular?

Yes, all four. ECharts is framework-agnostic — you bind it to a ref/element and call setOption. Maintained wrappers exist (e.g. echarts-for-react, vue-echarts) but are optional.

Q: How do I add a chart type that ECharts doesn't have?

Use the custom series (§4.7). It can render any shape your data describes. Several published extensions (gantt charts, hexbin maps, sunburst variants) are built this way.

Q: How do I limit the bundle size?

Switch from import * as echarts from 'echarts' to the tree-shaken pattern in §2.4 — import only the chart types and components you actually use. A typical dashboard with 4 chart types ships ~250 KB minified instead of ~1 MB.

Q: My chart is slow with 100K+ points. What can I do?

Several options:

  1. Switch the renderer to canvas (default) if you tried SVG.
  2. Set useDirtyRect: true on init for canvas.
  3. Use series.sampling: 'lttb' on line charts to downsample.
  4. Use progressive rendering on scatter/line: series.progressive: 5000.
  5. Disable animation: animation: false.
  6. Replace setOption with appendData for streaming inserts.

Q: Can I export a chart to PDF?

Indirectly. Export to PNG via getDataURL and embed it in a PDF using a library like jsPDF, or export to SVG via renderToSVGString and feed the SVG to a PDF generator that supports vector input.

Q: How do I detect when the chart has finished rendering / animating?

chart.on('finished', () => {
  // Animations complete, chart is at rest.
});
chart.on('rendered', (params) => {
  console.log('Frame elapsed:', params.elapsedTime, 'ms');
});

Q: Can I use SVG and Canvas for different elements in the same chart?

No. A chart instance has exactly one renderer. If you need both, create two charts.

Q: How do I get notified when a user zooms via the dataZoom slider?

chart.on('datazoom', (params) => {
  console.log('Range:', params.start, '-', params.end, '%');
});

Q: Why does my legend / dataset label show item indices instead of names?

Your series doesn't have a name, or your dataset doesn't have headers in the first row. Add name: 'Revenue' to each series, or dimensions: [...] / a header row to the dataset.

Q: How do I make a chart 100% responsive to its container?

Set the container CSS to flex or grid sizing, then attach a ResizeObserver:

const ro = new ResizeObserver(() => chart.resize());
ro.observe(container);
// On unmount:
ro.disconnect();
chart.dispose();

Q: Do I need to bundle map JSON for every country?

No. Register only the maps you actually display. Map definitions can be lazy-loaded:

async function loadCountryMap(name) {
  const geo = await fetch(`/maps/${name}.geo.json`).then(r => r.json());
  echarts.registerMap(name, geo);
}

Q: Where do I report a bug or request a feature?

Issues are tracked at github.com/apache/echarts/issues. Use the [Bug] or [Feature] template prefix. The community mailing list is [email removed].

Q: How is ECharts maintained?

ECharts is an Apache Software Foundation top-level project. It is governed by the ASF charter with a Project Management Committee, public mailing list, and Apache release process.


19. Where to Go Next

  • Architecture, internals, and extension authoring — see the Admin Guide. It covers the component model, render pipeline, building custom themes, plugin development, build flavors, and security considerations.
  • Live examples — echarts.apache.org/examples has 600+ runnable demos searchable by chart type.
  • Option reference — every option key with type signatures: echarts.apache.org/option.
  • Source — github.com/apache/echarts, Apache 2.0 license.