Echarts in React

Published on

If you are in the unfortunate, but all to common, position of needing to use echarts (which is great) within a React framework (this is the part I feel sorry for you about), maybe this will help. AI is likely to offer you a bunch of outdated BS if you ask it to help with this sort of task, and this post may end up as that too soon enough, but at the time of publishing there was not anything better that I was able to find, so it felt worth sharing.

A React wrapper component for Apache ECharts

ReactECharts.tsx

import type {
	ECharts,
	EChartsInitOpts,
	EChartsOption,
	EChartsType,
	SetOptionOpts,
} from "echarts";
import { init } from "echarts";
import type { CSSProperties } from "react";
import {
	forwardRef,
	useCallback,
	useEffect,
	useImperativeHandle,
	useRef,
} from "react";

import * as echarts from "echarts";
import chartTheme from "public/JSON/chartTheme.json" with { type: "json" };

// Register the theme ONCE at the module level
echarts.registerTheme("chartTheme", chartTheme);

export interface ReactEChartsProps {
	initOptions?: EChartsInitOpts;
	option: EChartsOption;
	style?: CSSProperties; // Custom CSS styles for the chart container
	settings?: SetOptionOpts; // Additional ECharts settings
	showLoading?: boolean; // Flag to show or hide the loading animation
	theme?: string | Record<string, any>; // Theme for the ECharts instance
	onEvents?: Record<
		string,
		(this: EChartsType, event: unknown) => boolean | void
	>; // Event handlers for ECharts events
}

export interface EChartsInstance {
	/** Method to get the current ECharts instance */
	getEchartsInstance: () => ECharts | null;
}

const ReactECharts = forwardRef<EChartsInstance, ReactEChartsProps>(
	(
		{
			initOptions = {},
			option,
			style = {},
			settings,
			showLoading = false,
			theme = "chartTheme",
			onEvents = {},
		}: ReactEChartsProps,
		ref
	) => {
		// Ref for the chart container div
		const chartRef = useRef<HTMLDivElement>(null);
		// Ref for the ECharts instance
		const chartInstance = useRef<ECharts | null>(null);

		// Expose the getEchartsInstance method to parent components
		useImperativeHandle(
			ref,
			() => ({
				getEchartsInstance: () => chartInstance.current,
			}),
			[]
		);

		const resizeChart = useCallback(() => {
			chartInstance.current?.resize();
		}, []);

		const initializeChart = useCallback(() => {
			if (chartRef.current !== null) {
				if (!chartInstance.current) {
					chartInstance.current = init(chartRef.current, theme, {
						width: "auto",
						height: "auto",
						...initOptions,
					});
				}

				chartInstance.current.setOption(option, settings);

				Object.keys(onEvents).forEach((eventName) => {
					chartInstance.current?.on(eventName, (...args) => {
						onEvents[eventName]?.apply(chartInstance.current, args);
					});
				});

				if (showLoading) {
					chartInstance.current.showLoading();
				} else {
					chartInstance.current.hideLoading();
				}

				window.addEventListener("resize", resizeChart);

				// Return cleanup function
				return () => {
					Object.keys(onEvents).forEach((eventName) => {
						chartInstance.current?.off(eventName, onEvents[eventName]);
					});

					chartInstance.current?.dispose();
					chartInstance.current = null;
					window.removeEventListener("resize", resizeChart);
				};
			}
		}, [
			initOptions,
			option,
			settings,
			theme,
			onEvents,
			showLoading,
			resizeChart,
		]);

		useEffect(() => {
			const cleanup = initializeChart();
			return cleanup;
		}, [initializeChart]);

		return (
			<div
				ref={chartRef}
				role="region"
				style={{ width: "100%", height: "100px", ...style }}
			/>
		);
	}
);

ReactECharts.displayName = "ReactECharts";

export default ReactECharts;

Then you can go about using this in other React components like so:

	// other react stuff here...

	const options: echarts.EChartsOption = {
		// options content here...
	};

	return (
		<ReactECharts
			option={options}
			showLoading={isLoading}
			style={{ minHeight: "750px" }}
		/>
	);

UPDATE:

See how this works with React v19 and eCharts v6.