@sentimony/echarts
You MUST use this when building, styling, debugging, or optimizing Apache ECharts charts in JavaScript, React, or Vue - setup, lifecycle, responsive resizing, theming, large datasets, streaming, SSR, and symptoms like a blank chart or broken resize. Not for choosing chart types or for other charting libraries.
| name | echarts |
| description | You MUST use this when building, styling, debugging, or optimizing Apache ECharts charts in JavaScript, React, or Vue - setup, lifecycle, responsive resizing, theming, large datasets, streaming, SSR, and symptoms like a blank chart or broken resize. Not for choosing chart types or for other charting libraries. |
| license | MIT |
| compatibility | Requires a JavaScript package manager; `echarts` must be installed in the target project (framework wrappers are optional). |
ECharts
Use this skill to build, audit, or fix Apache ECharts charts without turning the task into an option-reference lookup. Match the project's existing setup first; only introduce wrappers or new dependencies when the project has none.
Decision Tree
User task -> Does the project already use ECharts?
- Yes -> Find existing chart components/helpers, reuse their init, theme,
and resize patterns. Match import style (full vs echarts/core).
- No, and the task is an audit -> write an applicability check (section 0 of
references/audit.md) and stop; do not add the dependency
- No -> Pick integration by framework:
- React -> echarts-for-react wrapper, or a small hook around
init/dispose if the project avoids extra deps
- Vue 3 -> vue-echarts wrapper, or composable around init/dispose
- Vanilla / other -> echarts.init on a sized container
Next -> Bundle size a concern (app ships to users)?
- Yes -> Import from 'echarts/core' and register only the used charts,
components, and renderer (tree-shaking)
- No / internal tool / prototype -> import * as echarts from 'echarts'
Then -> Build the smallest working option, render it, then layer on
interactivity (tooltip, dataZoom, toolbox) and theming.
Core Workflow
- Inspect first: find existing ECharts usage, themes, and shared option helpers before writing a new chart.
- Size the container: the container element must have non-zero width and height before
echarts.initruns; a chart in a display:none or unmounted tab renders blank. - Own the lifecycle: one
initper container,resize()on container size change,dispose()on unmount. Wrappers handle this; hand-rolled code must. - Update via
setOption: default merge mode for incremental updates (streaming, new data);notMerge: truewhen the chart type or structure changes. - Verify visually: render the chart and check axes, labels, and tooltip against real data before polishing.
Setup
npm install echarts # core library (always)
npm install echarts-for-react # React wrapper (optional)
npm install vue-echarts # Vue 3 wrapper (optional)
Tree-shakeable imports for production bundles:
import * as echarts from 'echarts/core';
import { LineChart, BarChart } from 'echarts/charts';
import { GridComponent, TooltipComponent, DataZoomComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([LineChart, BarChart, GridComponent, TooltipComponent, DataZoomComponent, CanvasRenderer]);
A missing registration fails at runtime with a console error naming the missing chart/component; register it, do not switch to full import to silence the error. It is a console.error, not a thrown exception, so unit tests pass silently over it; catch it by asserting on the console or the rendered output.
With multiple chart components in one codebase, prefer a shared registration module (one echarts.use([...]) call imported everywhere) over per-component use lists; per-component lists drift out of sync and hide missing registrations until a component renders alone. Deliberate feature-specific registration in code-split routes is a valid exception for lazy-loaded dashboards.
Type imports: import type { ... } from 'echarts' is erased at compile time and does not affect the bundle; only value imports from the root package pull everything in. Some types (XAXisComponentOption, DefaultLabelFormatterCallbackParams) are exported only from the root, so mixing import type from 'echarts' with values from 'echarts/core' is normal; prefer ComposeOption from 'echarts/core' for option types:
import type { ComposeOption } from 'echarts/core';
import type { LineSeriesOption } from 'echarts/charts';
import type { GridComponentOption, TooltipComponentOption } from 'echarts/components';
type ChartOption = ComposeOption<LineSeriesOption | GridComponentOption | TooltipComponentOption>;
Lifecycle Rules
- Vanilla: keep the chart instance; call
chart.resize()from aResizeObserveron the container; callchart.dispose()before removing the container. - React (echarts-for-react): pass
optionas a prop; usenotMergeprop when replacing structure; get the instance viaref.getEchartsInstance()only for imperative needs (streamingsetOption,dispatchAction). - React (hand-rolled hook):
initin an effect,disposein its cleanup; keepoptionupdates in a separate effect so the chart is not re-created on every render. - Vue (vue-echarts): use
:optionbinding withautoresize; access the instance via template ref fordispatchAction. Pass:update-options="{ notMerge: true }"for structural option changes (chart type, series count, removing axes/series); merge mode keeps stale series. Switch themes via thethemeprop orTHEME_KEYinjection, notupdate-options(on older ECharts/vue-echarts versions, remount/re-init instead). Use thegroupprop to link charts (equivalent toecharts.connect). - Never call
echarts.inittwice on the same DOM node; reuse the instance or dispose first (echarts.getInstanceByDomto check).
Data and Options
- Prefer the
datasetcomponent (source+encode) when multiple series or charts share one table of data; use per-seriesdatafor simple single-series charts. - Time series: use
xAxis: { type: 'time' }with[timestamp, value]pairs instead of pre-formatting date strings into a category axis. - Large categorical axes: set
axisLabel.interval/rotatedeliberately instead of accepting overlap. - Tooltips:
trigger: 'axis'for line/bar time series,trigger: 'item'for pie/scatter/map. - Use
valueFormatterortooltip.formatterfor units; keep number formatting in one shared helper when the dashboard has many charts. - HTML tooltip
formatteroutput is injected as HTML: escape untrusted data (series names, user-generated labels) with a shared escape helper, or usetooltip.renderMode: 'richText'to opt out of HTML entirely.
Performance
- Choose Canvas, SVG, or WebGL from measured workload rather than a fixed point threshold. Measure the dataset, device/browser, interaction latency, and SVG output size; see the audit reference when reviewing an existing chart.
- For large line/scatter series: enable
large: trueandsampling: 'lttb'on the series; turn offanimationfor initial render of big datasets. - Millions of points: use
echarts-gl(WebGL), a separate dependency; add it only when actually needed. - Streaming: call
setOption({ series: [{ data }] })on the existing instance (merge mode); do not re-init or passnotMergeper tick. - Many charts on one page: share a single
ResizeObserver/resize handler and useecharts.connectfor linked tooltips/dataZoom instead of duplicating handlers.connectis also a UX feature for dashboards:chart.group = 'name'; echarts.connect('name')(or the vue-echartsgroupprop) syncs tooltips and dataZoom across related charts. Only link charts with compatible axis semantics (same x-axis type and domain); a chart with a different axis belongs in its own group or unlinked.
Theming
- Register a theme once (
echarts.registerTheme('name', themeObject)) and pass the name to everyinit; do not copy color arrays into each chart's option. - Dark mode: prefer
init(el, null, ...)plus a registered dark theme, ordarkMode: truein the option. Switch themes at runtime withchart.setTheme(...)(ECharts 6) or the vue-echartsthemeprop; on ECharts 5 themes are fixed at init time; re-init (dispose + init) there. - Keep chart-independent styling (font family, palette) in the theme; keep data-dependent styling (visualMap ranges, markLines) in the option.
SSR and Export
- Server-side rendering (reports, emails, OG images):
echarts.init(null, null, { renderer: 'svg', ssr: true, width, height })thenrenderToSVGString()- Node only, no DOM needed. - If option builders are shared between the browser and a Node SVG renderer, keep both
echarts.use([...])registration points covering the same set; a narrower server-side list silently renders without the missing components. - Client image export: enable
toolbox.feature.saveAsImage, or callchart.getDataURL({ pixelRatio: 2 })programmatically.
ECharts 6 Migration Notes
grid.containLabelis deprecated. The semantics-preserving migration iscontainLabel: true→{ outerBoundsMode: 'same', outerBoundsContain: 'axisLabel' }; setgrid.outerBoundsonly when you need a custom constraint rect (it is a separate part of the new layout API). The legacy behavior still works only ifLegacyGridContainLabel(from'echarts/features') is registered; treat remainingcontainLabel: trueusages as tech debt when auditing.- The default theme changed in v6 (palette and component layout). To keep the v5 look during migration:
import 'echarts/theme/v5'and pass'v5'as the theme toinit. - Axis label overflow prevention and axis-name overlap prevention are on by default in v6, which can shift layouts slightly; disable with
grid.outerBoundsMode: 'none'andxAxis/yAxis.nameMoveOverlap: falsewhen pixel-parity with v5 matters. - Check the installed major version (
node_modules/echarts/package.json) before recommending options; deprecations surface as console warnings, not errors.
Auditing Existing Usage
For a code-and-browser audit, read references/audit.md before writing findings. It is the required full checklist for dashboard growth, tree-shaken registrations, interactive state, HTML tooltip trust, large-data cardinality, zero-size failures, and browser evidence. When the chart instance is not reachable from the page (production bundle, wrapper without an exposed instance), section 4 and section 8 of the reference describe DOM proxies that stand in for getOption().
Quick triage still starts with the shared registration module, lifecycle ownership, structural setOption updates, root value imports, and ECharts-version migration debt. Treat repeated formatter/options as extraction debt; centralized design tokens passed directly to options are a valid alternative to registerTheme when that is the project's deliberate convention.
Common Failure Modes
- Blank chart, no error: container had zero size at init (hidden tab, flex parent without height, init before mount). Fix sizing/timing, then call
resize(). - Chart does not update: a new option object with merge mode silently keeps stale series/axes; use
notMerge: truewhen removing series or changing chart type. - Legend/dataZoom selection lost after update:
notMerge: truecan reset interactive state, depending on the wrapper, versions, and update path. Capture the state you need to survive (chart.getOption().legend[0].selected, the dataZoom range) and pass it back, or give it an explicit app-side owner. Do not report a reset from static inspection alone; prove it on the installed ECharts/wrapper versions. The ECharts instance is a valid owner for session-only state when browser evidence shows it survives and the product does not require it to survive a remount or navigation. notMerge: trueeverywhere: forfeits ECharts' diff optimization and risks resetting legend/dataZoom selection on structural updates. Reserve it for structural changes (chart type, series count, removed axes/series); keep merge mode for data-only updates.- "Component xxx not exists" / missing chart: tree-shaken build without the registration; add it to
echarts.use([...]). - Memory growth in SPA: instances not disposed on route change; verify
dispose()runs in unmount cleanup. - Chart wrong size after sidebar/panel toggle: window
resizeevent never fired; observe the container (ResizeObserver /autoresize), not the window. - Tooltip clipped: set
tooltip.confine: trueorappendToBody-styletooltip.appendTowhen the chart sits in an overflow-hidden container. - Sluggish with big data: animation on + no sampling; set
animation: false,sampling: 'lttb',large: truebefore reaching for WebGL.
Reference Examples
examples/vanilla_line.html- Vanilla JS time-series line chart with resize handlingexamples/react_chart.tsx- React component with tree-shaken imports and echarts-for-reactexamples/vue_chart.vue- Vue 3 component using vue-echarts with autoresize
Loading...
Select a file to preview
Analyzing security...
Checking scan reports and verification data.
Bill of Materials
Everything this skill can do — files, network, commands, and more.