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.