ECharts cheat sheet
A scannable ECharts reference: 20 short snippets across 8 topics, each linking back to the lesson it came from.
At a glance
| Topic | What it covers | |
|---|---|---|
| Option configuration | A dataset keeps rows in one place and lets each series declare which column is the dimension and which are the values | lesson |
| Common chart types | Line, bar and pie as the baseline, the wider family of series types, and the per-series options that change how data | lesson |
| Interaction, resize and data updates | Listening to window.resize misses every layout change that is not a viewport change: sidebars, split panes and CSS grid | lesson |
| 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 | lesson |
| Styling, themes and dark mode | The practical rule for a design system: describe everything shared in a theme object, and keep only per-chart variation | lesson |
| Tooltips, legends, labels and visual maps | Write formatter callbacks that produce useful tooltips, manage label overlap, and drive colour from data with | lesson |
| ECharts in React, Vue and Angular | In every framework the same three rules apply: create the chart after the DOM exists, store the instance outside the | lesson |
| Accessibility, export and printing | Generate a text description with the aria option, add decal patterns so colour is not the only channel, export images | lesson |
Quick snippets
Option configuration
The option tree
chart.setOption({
title: { text: 'Weekly signups', left: 'center' },
tooltip: { trigger: 'axis' },
legend: { bottom: 0 },
grid: { left: 48, right: 24, top: 56, bottom: 48 },
xAxis: { type: 'category', boundaryGap: false },
yAxis: { type: 'value', name: 'users' },
series: [
{ name: 'Free', type: 'line', areaStyle: {}, smooth: true, data: [820, 932, 901, 1290] },
{ name: 'Paid', type: 'line', areaStyle: {}, smooth: true, data: [120, 132, 141, 154] }
]
});
Initialise and setOption
<div id="main" style="width: 100%; height: 360px;"></div>
<script type="module">
import * as echarts from 'https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.esm.min.js';
const chart = echarts.init(document.getElementById('main'), null, {
renderer: 'canvas', // or 'svg' for large static dashboards and print
useDirtyRect: true // cheaper partial repaints while animating
});
chart.setOption({
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu'] },
yAxis: { type: 'value' },… 6 more lines in the full lesson.
Dataset and encode
chart.setOption({
dataset: {
source: [
['week', 'free', 'paid'],
['W1', 820, 120],
['W2', 932, 132],
['W3', 901, 141],
['W4', 1290, 154]
]
},
xAxis: { type: 'category' },
yAxis: { type: 'value' },… 8 more lines in the full lesson.
Full lesson: Option configuration →
Common chart types
Line, bar and pie
chart.setOption({
tooltip: { trigger: 'axis' },
legend: { bottom: 0 },
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
series: [
{ name: 'Revenue', type: 'bar', data: [120, 200, 150, 80], barMaxWidth: 32 },
{ name: 'Target', type: 'line', data: [110, 180, 170, 120], smooth: true },
{ name: 'Mix', type: 'pie', radius: ['45%', '70%'], center: ['82%', '38%'],
data: [
{ value: 1048, name: 'Direct' },
{ value: 735, name: 'Search' },… 4 more lines in the full lesson.
Beyond the basics
// scatter with an intensity legend
chart.setOption({
xAxis: {},
yAxis: {},
visualMap: {
min: 0, max: 100, dimension: 2, calculable: true,
inRange: { color: ['#bfdbfe', '#1d4ed8'] }
},
series: [{
type: 'scatter',
symbolSize: (data) => Math.sqrt(data[2]) * 2,
data: [[10, 8, 20], [15, 20, 55], [22, 14, 90]]… 2 more lines in the full lesson.
Per-series options that matter
series: [
{ name: 'Direct', type: 'bar', stack: 'traffic', data: [320, 302, 301] },
{ name: 'Search', type: 'bar', stack: 'traffic', data: [120, 132, 101] },
{ name: 'Rate', type: 'line', yAxisIndex: 1, smooth: true,
data: [0.12, 0.16, 0.14], label: { show: true, formatter: '{c}' } },
{
name: 'Orders',
type: 'bar',
barGap: '10%',
barCategoryGap: '30%',
emphasis: { focus: 'series' },
data: [90, 110, 95]… 2 more lines in the full lesson.
Full lesson: Common chart types →
Interaction, resize and data updates
Events and actions
// listen: params carries the data point and the series that produced it
chart.on('click', (params) => {
console.log(params.seriesName, params.name, params.value, params.dataIndex);
openDetail(params.data.id);
});
chart.on('legendselectchanged', (params) => {
saveVisibility(params.selected); // { Free: false, Paid: true }
});
// dispatch: drive the chart programmatically
chart.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: 3 });… 3 more lines in the full lesson.
Responsive resize
// simplest correct approach: observe the container, not the window
const ro = new ResizeObserver(() => chart.resize());
ro.observe(document.getElementById('main'));
// stop observing when the chart is torn down
// ro.disconnect(); chart.dispose();
// option-level responsiveness: overrides keyed on container width
chart.setOption({
baseOption: {
legend: { bottom: 0 },
series: [{ type: 'line', symbol: 'circle' }]… 12 more lines in the full lesson.
Updating data
// merge (default): matched series are updated in place
chart.setOption({ series: [{ data: nextValues }] });
// explicitly replace one component type instead of merging
chart.setOption({ series: [{ data: nextValues }] }, { replaceMerge: ['series'] });
// full rebuild: nothing from the previous option survives
chart.setOption(freshOption, { notMerge: true });
// defer the render to the next frame when several updates arrive together
chart.setOption(patch, { lazyUpdate: true });
… 2 more lines in the full lesson.
Full lesson: Interaction, resize and data updates →
Getting started: install, init and lifecycle
Install and import
npm install echarts
# the full build is about 1 MB minified; the tree-shaken path is far smaller
init, container sizing and rendering mode
<div id="revenue" style="width: 100%; height: 360px;"></div>
Install and import
// 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';… 12 more lines in the full lesson.
Full lesson: Getting started: install, init and lifecycle →
Styling, themes and dark mode
Switching at runtime
// For a light/dark palette that changes together with the site, use a
// dynamic theme: a function that returns the theme based on a value.
echarts.registerTheme('adaptive', (value) => {
const dark = value === 'dark';
return {
color: dark
? ['#a78bfa', '#38bdf8', '#fbbf24', '#34d399', '#f87171']
: ['#6d28d9', '#0ea5e9', '#f59e0b', '#10b981', '#ef4444'],
textStyle: { color: dark ? '#e2e8f0' : '#0f172a' },
valueAxis: {
axisLabel: { color: dark ? '#94a3b8' : '#475569' },
splitLine: { lineStyle: { color: dark ? '#1e293b' : '#e2e8f0' } }… 12 more lines in the full lesson.
Switching at runtime
// When the site theme is already a set of CSS custom properties, read them
// and build the chart colours from the same tokens. One source of truth.
function themeFromCss(root = document.documentElement) {
const styles = getComputedStyle(root);
const token = (name, fallback) => styles.getPropertyValue(name).trim() || fallback;
return {
color: [
token('--chart-1', '#6d28d9'),
token('--chart-2', '#0ea5e9'),
token('--chart-3', '#f59e0b')
],… 16 more lines in the full lesson.
Overriding individual visuals
// Anything the theme sets can be overridden at the option level, and the
// option wins. This is how a single chart deviates from the house style.
const option = {
color: ['#0f766e'], // override the palette for this chart
textStyle: { fontFamily: 'Georgia, serif' },
legend: { textStyle: { color: '#0f172a', fontWeight: 600 } },
series: [{
type: 'line',
smooth: true,
lineStyle: { width: 3, color: '#0f766e' },
itemStyle: { color: '#0f766e', borderColor: '#fff', borderWidth: 2 },
areaStyle: {… 16 more lines in the full lesson.
Full lesson: Styling, themes and dark mode →
Tooltips, legends, labels and visual maps
visualMap, markLine and markArea
// A richer label formatter: rich text lets one label mix styles.
const richLabel = {
label: {
show: true,
formatter: '{name|{b}}
{value|{c} ms}',
rich: {
name: { color: '#64748b', fontSize: 11, lineHeight: 16 },
value: { color: '#0f172a', fontSize: 14, fontWeight: 600, lineHeight: 20 }
}
}
};… 9 more lines in the full lesson.
Full lesson: Tooltips, legends, labels and visual maps →
ECharts in React, Vue and Angular
Angular
// Server rendering: ECharts needs a DOM. Two workable approaches.
//
// 1. Skip the chart on the server and render it after hydration.
// In Angular: afterNextRender, as above. In React: guard with a mounted flag.
//
// 2. Render the chart on the server and ship the SVG output as markup.
// The chart is static until the client hydrates, but the layout is correct
// on first paint and the data is crawlable.
// React: a mounted flag keeps the server output and the first client render identical.
import { useState, useEffect } from 'react';
… 7 more lines in the full lesson.
Full lesson: ECharts in React, Vue and Angular →
Accessibility, export and printing
Decal patterns and focus
/* Give the chart container a visible focus style and a sensible role. */
.chart-container:focus-visible {
outline: 2px solid var(--brand, #6d28d9);
outline-offset: 2px;
}
/* Never hide a chart from assistive tech with aria-hidden when it carries data. */
.chart-container { position: relative; }
/* Respect reduced motion: ECharts listens to its own option, not to CSS. */
@media (prefers-reduced-motion: reduce) {
/* Nothing to do in CSS — set animation: false in the option instead. */… 1 more lines in the full lesson.
Decal patterns and focus
// Honour the reduced-motion preference in the option itself.
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function animationOption(matches) {
return matches
? { animation: false }
: { animation: true, animationDuration: 400, animationEasing: 'cubicOut' };
}
chart.setOption({
...baseOption,
...animationOption(prefersReducedMotion.matches)… 5 more lines in the full lesson.
Export and printing
// A print path that produces a usable page rather than a screenshot.
window.addEventListener('beforeprint', () => {
// Re-render at a higher device pixel ratio so the printed output is sharp.
chart.setOption({}, false);
chart.resize({ width: 900, height: 500 });
});
window.addEventListener('afterprint', () => {
chart.resize(); // back to the container's size
});
// If the chart is off-screen when the user prints, it may never have rendered.… 6 more lines in the full lesson.
Full lesson: Accessibility, export and printing →
FAQ
Is this ECharts cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
HTML CSS JavaScript TypeScript HTML DOM AJAX
Last refreshed 2026-09-27.