# Apache ECharts User Guide

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.

```html
<!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>
```

```text
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"

    ```bash
    npm install echarts
    ```

=== "yarn"

    ```bash
    yarn add echarts
    ```

=== "pnpm"

    ```bash
    pnpm add echarts
    ```

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

```javascript
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)"

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

=== "unpkg"

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

```html
<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](https://github.com/apache/echarts/releases) 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.

```javascript
// 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:

```javascript
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.

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

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

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

```text
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.

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

```javascript
const chart = echarts.init(dom, theme, opts);
```

| Parameter | Type | Default | Purpose |
|---|---|---|---|
| `dom` | `HTMLElement | null` | required (unless `opts.ssr`) | The container. Pass `null` only when `ssr: true`. |
| `theme` | `string | object | null` | `null` | A theme name registered via `registerTheme`, or a theme object. |
| `opts.renderer` | `'canvas' | 'svg'` | `'canvas'` | Which render backend to use. |
| `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` | Override container width. |
| `opts.height` | `number | 'auto'` | DOM `clientHeight` | Override container height. |
| `opts.locale` | `string` | `'EN'` | Locale code (see §11). |
| `opts.useCoarsePointer` | `boolean | 'auto'` | `'auto'` | Treat hover regions as coarse for touch devices. |
| `opts.pointerSize` | `number` | unset | Hover hit-area expansion in pixels. |
| `opts.ssr` | `boolean` | `false` | Server-side rendering mode (no DOM, returns SVG string). |

```javascript
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.

```javascript
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]
    }
  ]
});
```

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

```javascript
// 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).

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

For modern apps, ResizeObserver is more precise:

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

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

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

```javascript
chart.dispose();
```

Or dispose by DOM reference:

```javascript
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](https://echarts.apache.org/examples).

### 4.1 Cartesian basics

#### Line

```javascript
{
  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

```javascript
{
  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:

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

```javascript
{
  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

```javascript
{
  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:

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

#### Heatmap

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

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

### 4.3 Hierarchical

#### Pie

```javascript
{
  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:

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

#### Treemap

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

```javascript
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)

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

```javascript
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).

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

```javascript
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):

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

```javascript
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.

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

Items can be primitives, arrays, or objects:

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

```javascript
{
  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:

```javascript
{
  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:

```javascript
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.

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

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

```javascript
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.

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

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

### 6.4 Brush (range selection)

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

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

### 6.6 Events

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

```javascript
// 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` |

```javascript
{
  grid: { left: '10%', right: '5%', top: 60, bottom: 50 },
  xAxis: { type: 'value' },
  yAxis: { type: 'category' },
  series: [{ type: 'bar', data: [...] }]
}
```

### 7.1 Axis types

```javascript
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.

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

```javascript
{
  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:

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

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

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

Or use a function to color by data:

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

### 8.4 Dark mode

```javascript
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.

```javascript
{
  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:

```javascript
{ 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:

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

Or globally:

```javascript
{ labelLayout: { hideOverlap: true } }
```

---

## 10. Maps (Geo)

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

### 10.1 Registering

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

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

Or as a series itself:

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

```javascript
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.

```javascript
import 'echarts/i18n/langDE.js';

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

Or register from JSON:

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

Locale codes shipped (subset shown — full list in [i18n folder](https://github.com/apache/echarts/tree/master/i18n)):

| 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

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

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

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

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

```javascript
{
  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:

```javascript
{
  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:

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

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

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

### 16.3 Sync zoom across multiple charts

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

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

### 16.5 Render to a fresh canvas (snapshot)

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

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

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

Register a custom loading effect:

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

### 16.8 Get / inspect the current option

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

### 16.9 Replace a single mainType cleanly

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

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

```javascript
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:**

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

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

```javascript
// 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](https://github.com/apache/echarts/issues/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](https://github.com/apache/echarts/issues/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](https://github.com/apache/echarts/issues/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?**

```javascript
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?**

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

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

```javascript
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](https://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](apache__echarts__AdminGuide.md). It covers the component model, render pipeline, building custom themes, plugin development, build flavors, and security considerations.
- **Live examples** — [echarts.apache.org/examples](https://echarts.apache.org/examples) has 600+ runnable demos searchable by chart type.
- **Option reference** — every option key with type signatures: [echarts.apache.org/option](https://echarts.apache.org/option).
- **Source** — [github.com/apache/echarts](https://github.com/apache/echarts), Apache 2.0 license.

---

_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. Unofficial; not affiliated with or reviewed by the project._
