JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 409
  • Score
    100M100P100Q85253F
  • License MIT

React hooks & component for Apache ECharts — TypeScript, auto-resize, themes, lazy init

Package Exports

  • react-use-echarts
  • react-use-echarts/themes/registry

Readme

react-use-echarts

中文 | English

NPM version NPM downloads CI codecov GitHub Actions Workflow Status GitHub issues GitHub pull requests GitHub license

React hooks & component for Apache ECharts — TypeScript, auto-resize, themes, lazy init.

Features

  • Hook + Component — use useEcharts hook or the declarative <EChart /> component
  • TypeScript first — complete type definitions with IDE autocomplete
  • Zero dependencies — no runtime deps beyond peer deps
  • Auto-resize — handles container resizing via ResizeObserver
  • Themes — built-in light, dark, and macarons themes, plus any custom theme
  • Chart linkage — connect multiple charts for synchronized interactions
  • Lazy initialization — defer chart init until element enters viewport
  • StrictMode safe — instance cache with reference counting handles double mount/unmount

Requirements

  • React 19+ (react + react-dom)
  • ECharts 6.x
  • Node.js 22+ (required only for tooling/SSR frameworks — the published bundle is browser ESM)

CSR only. ECharts needs a live DOM; SSR is not supported.

ESM-only since 1.3.0. The package publishes a single ESM build (dist/index.js). Every modern bundler (Vite, Next.js, webpack 5+, Rspack, Parcel, Turbopack) and Node 22+ (require(ESM)) consume it natively. If you still depend on CJS-only tooling, pin to 1.2.x.

Installation

npm install react-use-echarts echarts
# or
yarn add react-use-echarts echarts
# or
pnpm add react-use-echarts echarts

Quick Start

<EChart /> Component

The simplest way — no ref needed:

import { EChart } from "react-use-echarts";

function MyChart() {
  return (
    <EChart
      option={{
        xAxis: { type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri"] },
        yAxis: { type: "value" },
        series: [{ data: [150, 230, 224, 218, 135], type: "line" }],
      }}
    />
  );
}

<EChart /> defaults to width: 100% and height: 100%, so the parent container still needs an explicit height.

Pass ref to access the imperative API — see Returns for the full list (setOption, dispatchAction, clear, resize, appendData, getDataURL, convertToPixel, …).

useEcharts Hook

For full control, use the hook directly:

import { useRef } from "react";
import { useEcharts } from "react-use-echarts";

function MyChart() {
  const chartRef = useRef<HTMLDivElement>(null);
  const { setOption, getInstance, resize } = useEcharts(chartRef, {
    option: { series: [{ type: "line", data: [150, 230, 224, 218, 135] }] },
  });
  return <div ref={chartRef} style={{ width: "100%", height: "400px" }} />;
}

The chart container must have an explicit size, for example style={{ width: "100%", height: "400px" }}.

Recipes

Themes

Built-in themes require one-time registration at app startup:

import { registerBuiltinThemes } from "react-use-echarts/themes/registry";
registerBuiltinThemes();

// Built-in theme
useEcharts(chartRef, { option, theme: "dark" });

// Any string registered via echarts.registerTheme
useEcharts(chartRef, { option, theme: "vintage" });

// Custom theme object (use useMemo to keep reference stable)
const customTheme = useMemo(() => ({ color: ["#fc8452", "#9a60b4", "#ea7ccc"] }), []);
useEcharts(chartRef, { option, theme: customTheme });

Event Handling

Supports shorthand (function) and full config (object with query/context):

useEcharts(chartRef, {
  option,
  onEvents: {
    click: (params) => console.log("Clicked:", params),
    mouseover: {
      handler: (params) => console.log("Hover:", params),
      query: "series",
    },
  },
});

Loading State

const [loading, setLoading] = useState(true);

useEcharts(chartRef, {
  option,
  showLoading: loading,
  loadingOption: { text: "Loading..." },
});

Chart Linkage

Assign the same group ID — tooltips, highlights, and other interactions will sync:

useEcharts(chartRef1, { option: option1, group: "dashboard" });
useEcharts(chartRef2, { option: option2, group: "dashboard" });

Lazy Initialization

Defer chart init until the element scrolls into view:

useEcharts(chartRef, { option, lazyInit: true });

// Custom IntersectionObserver options
useEcharts(chartRef, {
  option,
  lazyInit: { rootMargin: "200px", threshold: 0.5 },
});

Use with Next.js (App Router)

The package entry and themes/registry are marked with "use client", so importing them inside any React Server Component file does not bundle ECharts into the server payload. Wrap the chart in your own client component and import it from any Server Component:

// app/components/MyChart.tsx
"use client";
import { EChart } from "react-use-echarts";

export function MyChart() {
  return <EChart option={{ series: [{ type: "line", data: [1, 2, 3] }] }} />;
}
// app/page.tsx (Server Component) — imports the Client Component directly
import { MyChart } from "./components/MyChart";

export default function Page() {
  return <MyChart />;
}

Pages Router only: if you need to load the chart inside getServerSideProps / getStaticProps pages and force client-only rendering, use dynamic(() => import("./components/MyChart").then((m) => m.MyChart), { ssr: false }). In the App Router, next/dynamic with ssr: false is disallowed inside Server Components — the "use client" directive already does the right thing.

Gotchas

  • Container needs explicit size — the chart won't render in a zero-height div; give the container height (and width if not 100%).
  • Keep onEvents reference stable — a new onEvents object on each render triggers a full rebind. Memoize it with useMemo (or hoist) when handlers don't change.
  • Don't share one DOM element across multiple useEcharts hooks — the instance cache reuses a single ECharts instance and emits a dev warning; updates from different hooks will overwrite each other.
  • initOpts and custom theme objects recreate the instance on reference change — pass memoized or module-level constants unless recreation is intended.
  • StrictMode is safe — double mount/unmount is handled by the reference-counted instance cache.

API Reference

<EChart /> Props

Declarative component wrapping useEcharts. Accepts all hook options as props plus:

Prop Type Default Description
style React.CSSProperties { width: '100%', height: '100%' } Container style (merged with defaults)
className string Container CSS class
ref Ref<UseEchartsReturn> Exposes the full imperative API (see Returns)

useEcharts(ref, options)

Options

Option Type Default Description
option EChartsOption (required) ECharts configuration
theme string | object Any registered theme name, or custom theme object
renderer 'canvas' | 'svg' 'canvas' Renderer type
lazyInit boolean | IntersectionObserverInit false Lazy initialization via IntersectionObserver
group string Chart linkage group ID
setOptionOpts SetOptionOpts Default options for setOption calls
showLoading boolean false Show loading indicator
loadingOption object Loading indicator configuration
onEvents EChartsEvents Event handlers (fn or { handler, query?, context? })
autoResize boolean true Auto-resize via ResizeObserver
initOpts EChartsInitOpts Passed to echarts.init() (devicePixelRatio, locale, width, etc.)
onError (error: unknown) => void Error handler — effect failures logged via console.error without it; imperative setOption throws without it

Returns

Prefer the declarative props (option, theme, showLoading, …) over imperative methods. Use these methods only when a prop does not cover the action — image export, coordinate conversion, streaming append, etc. All methods are no-ops or return safe defaults when the instance is not yet initialized. When the instance throws, errors are routed through onError if provided (and the call returns the fallback); otherwise the error is rethrown — including from readers (no console.error fallback for imperative methods).

Lifecycle / updates

Method Type Description
setOption (option: EChartsOption, opts?: SetOptionOpts) => void Update chart configuration
dispatchAction (payload: Payload, opt?: boolean | { silent?: boolean; flush?: boolean }) => void Dispatch an ECharts action (highlight, downplay, showTip, etc.)
clear () => void Clear current chart content
resize (opts?: ResizeOpts) => void Manually trigger chart resize. ResizeOpts accepts width/height/animation/silent
appendData (params: { seriesIndex: number; data: ArrayLike<unknown> }) => void Append data to a series (streaming). Drift-aware: drops dedup memory so a subsequent shallow-equal-but-new-ref option rerender re-applies setOption

Read / introspect

Method Type Description
getInstance () => ECharts | undefined Get ECharts instance
getOption () => EChartsOption | undefined Get the current merged option
getWidth () => number | undefined Container width in pixels
getHeight () => number | undefined Container height in pixels
getDom () => HTMLElement | undefined Underlying DOM container
isDisposed () => boolean Whether the instance is disposed (returns true when uninitialized — semantically gone)

Export

Method Type Description
getDataURL (opts?) => string | undefined Base64 image data URL (png / jpeg / svg)
getConnectedDataURL (opts?) => string | undefined Combined image of all charts in the same group
renderToSVGString (opts?: { useViewBox?: boolean }) => string | undefined Render chart to SVG string (works with the SVG renderer)
getSvgDataURL () => string | undefined Get SVG data URL of the current chart

Coordinate conversion

Method Type Description
convertToPixel (finder: ChartFinder, value: ChartScaleValue | ChartScaleValue[]) => number | number[] | undefined Logical → pixel coordinates
convertFromPixel (finder: ChartFinder, value: number | number[]) => number | number[] | undefined Pixel → logical coordinates
containPixel (finder: ChartFinder, value: number[]) => boolean Whether a pixel point is inside the matched component (false when uninit)

ChartFinder is string | { seriesIndex?, seriesId?, …, geoIndex?, … } — a string shorthand or a model finder object. ChartScaleValue is number | string | Date.

Other Exports

import { useLazyInit } from "react-use-echarts"; // standalone lazy init hook
import { isBuiltinTheme, registerCustomTheme } from "react-use-echarts"; // theme utils (no JSON)
import { registerBuiltinThemes } from "react-use-echarts/themes/registry"; // ~20KB theme JSON

// All exported types: UseEchartsOptions, UseEchartsReturn, EChartProps,
// EChartsEvents, EChartsEventConfig, EChartsInitOpts, BuiltinTheme, LoadingOption,
// ChartFinder, ChartScaleValue
// EChartsOption, SetOptionOpts, ResizeOpts come from the "echarts" package directly.

Migrating from echarts-for-react

Most props map 1:1; a few are folded into existing options. Quick reference:

echarts-for-react react-use-echarts Notes
option option Same
theme theme Same; built-in themes need registerBuiltinThemes() first (see Themes)
notMerge / lazyUpdate setOptionOpts: { notMerge, lazyUpdate } Folded into a single object passed to setOption
showLoading showLoading Same
loadingOption loadingOption Same
onEvents onEvents Same shape; also accepts { handler, query?, context? } for query/context binding
onChartReady Use the imperative API Read getInstance() from the hook return (or ref.current) — fires after first init
opts.renderer renderer: 'canvas' | 'svg' Promoted to a top-level option
opts (rest) initOpts Same shape (devicePixelRatio, locale, width, height, useDirtyRect, etc.)
style style <EChart /> defaults to { width: '100%', height: '100%' } so the parent needs size
className className Same
lazyUpdate (top-level) setOptionOpts: { lazyUpdate: true } See notMerge row
shouldSetOption Gate the option prop yourself Top-level keys are deduped via shallowEqual automatically; for custom predicates (deep compare, throttling, app-state gating) memoize/skip the option prop in the parent component
autoResize (4.x) autoResize Same default (true); resize uses ResizeObserver + RAF
none lazyInit New: defer init until the container scrolls into viewport
none group New: chart linkage via shared group ID
none onError New: route init / setOption / dispatchAction errors through a callback

Side-by-side example:

// echarts-for-react
<ReactECharts
  option={option}
  theme="dark"
  notMerge
  lazyUpdate
  opts={{ renderer: "svg", devicePixelRatio: 2 }}
  onEvents={{ click: handleClick }}
  showLoading={loading}
  onChartReady={(instance) => instanceRef.current = instance}
/>

// react-use-echarts
<EChart
  ref={chartRef}
  option={option}
  theme="dark"
  setOptionOpts={{ notMerge: true, lazyUpdate: true }}
  renderer="svg"
  initOpts={{ devicePixelRatio: 2 }}
  onEvents={{ click: handleClick }}
  showLoading={loading}
/>
// chartRef.current?.getInstance() replaces onChartReady

Contributing

We welcome all contributions. Please read the contributing guidelines first.

Changelog

Detailed changes for each release are documented in the release notes.

License

MIT © Ethan