Vue.js component for Apache ECharts™ providing declarative data visualization with Vue 2.7+ and Vue 3.1+ support
—
The core Vue component that renders ECharts instances with reactive props, comprehensive event handling, and automatic lifecycle management.
The main Vue component for rendering ECharts. Provides reactive chart updates, event handling, and full access to ECharts API methods.
/**
* Vue component for rendering ECharts with reactive props
* Automatically manages chart lifecycle and provides method access
*/
declare const VChart: DefineComponent<
ChartProps & ChartEventProps,
{
root: Ref<HTMLElement | undefined>;
chart: Ref<EChartsType | undefined>;
},
{},
{},
ChartMethods
>;
interface ChartProps {
/** ECharts option configuration object */
option?: Option;
/** Chart theme (object or string name like 'dark') */
theme?: Theme;
/** ECharts initialization options */
initOptions?: InitOptions;
/** Chart update options */
updateOptions?: UpdateOptions;
/** Auto-resize configuration */
autoresize?: AutoResize;
/** Loading state */
loading?: boolean;
/** Loading animation options */
loadingOptions?: LoadingOptions;
/** Chart group name for linking charts */
group?: string;
/** Manual update mode - prevents automatic option watching */
manualUpdate?: boolean;
}Usage Examples:
<template>
<v-chart
:option="chartOption"
:theme="chartTheme"
:autoresize="true"
:loading="isLoading"
@click="handleClick"
class="chart"
/>
</template>
<script setup>
import VChart from "vue-echarts";
import { ref } from "vue";
const chartOption = ref({
xAxis: { type: "category", data: ["A", "B", "C"] },
yAxis: { type: "value" },
series: [{ data: [120, 200, 150], type: "bar" }]
});
const chartTheme = ref("dark");
const isLoading = ref(false);
function handleClick(params) {
console.log("Chart clicked:", params);
}
</script>Vue-ECharts supports all ECharts events through Vue event props. Event names are converted to Vue's on* format.
type ChartEventProps = {
[key in keyof Emits as key extends string
? `on${Capitalize<key>}`
: never]?: Emits[key];
};
interface Emits {
// Mouse events
click: (params: ECElementEvent) => void;
dblclick: (params: ECElementEvent) => void;
mousedown: (params: ECElementEvent) => void;
mousemove: (params: ECElementEvent) => void;
mouseup: (params: ECElementEvent) => void;
mouseover: (params: ECElementEvent) => void;
mouseout: (params: ECElementEvent) => void;
contextmenu: (params: ECElementEvent) => void;
globalout: (params: ECElementEvent) => void;
// ZRender events (with zr: prefix)
"zr:click": (params: ElementEvent) => void;
"zr:mousewheel": (params: ElementEvent) => void;
"zr:drag": (params: ElementEvent) => void;
"zr:dragstart": (params: ElementEvent) => void;
"zr:dragend": (params: ElementEvent) => void;
"zr:dragenter": (params: ElementEvent) => void;
"zr:dragleave": (params: ElementEvent) => void;
"zr:dragover": (params: ElementEvent) => void;
"zr:drop": (params: ElementEvent) => void;
"zr:mousedown": (params: ElementEvent) => void;
"zr:mouseup": (params: ElementEvent) => void;
"zr:dblclick": (params: ElementEvent) => void;
"zr:contextmenu": (params: ElementEvent) => void;
// Chart-specific events
highlight: (params: any) => void;
downplay: (params: any) => void;
selectchanged: (params: any) => void;
legendselectchanged: (params: any) => void;
legendselected: (params: any) => void;
legendunselected: (params: any) => void;
legendselectall: (params: any) => void;
legendinverseselect: (params: any) => void;
legendscroll: (params: any) => void;
datazoom: (params: any) => void;
datarangeselected: (params: any) => void;
graphroam: (params: any) => void;
georoam: (params: any) => void;
treeroam: (params: any) => void;
timelinechanged: (params: any) => void;
timelineplaychanged: (params: any) => void;
restore: (params: any) => void;
dataviewchanged: (params: any) => void;
magictypechanged: (params: any) => void;
geoselectchanged: (params: any) => void;
geoselected: (params: any) => void;
geounselected: (params: any) => void;
axisareaselected: (params: any) => void;
brush: (params: any) => void;
brushEnd: (params: any) => void;
brushselected: (params: any) => void;
globalcursortaken: (params: any) => void;
// Lifecycle events
rendered: (params: { elapsedTime: number }) => void;
finished: () => void;
}Usage Examples:
<template>
<v-chart
@click="handleClick"
@legend-select-changed="handleLegendChange"
@zr:click="handleZrClick"
@rendered="handleRendered"
/>
</template>
<script setup>
function handleClick(params) {
console.log("Data item clicked:", params.data);
}
function handleLegendChange(params) {
console.log("Legend selection changed:", params.selected);
}
function handleZrClick(params) {
console.log("Canvas clicked:", params);
}
function handleRendered(params) {
console.log("Chart rendered in:", params.elapsedTime, "ms");
}
</script>All ECharts instance methods are exposed through the component for programmatic control.
interface ChartMethods {
/** Get chart width in pixels */
getWidth(): number;
/** Get chart height in pixels */
getHeight(): number;
/** Get chart DOM element */
getDom(): HTMLElement;
/** Get current chart option */
getOption(): Option;
/** Resize chart to fit container */
resize(): void;
/** Dispatch action to chart */
dispatchAction(payload: any): void;
/** Convert logical coordinate to pixel coordinate */
convertToPixel(finder: any, value: any): number[] | number;
/** Convert pixel coordinate to logical coordinate */
convertFromPixel(finder: any, value: any): number[] | number;
/** Check if pixel coordinate is in specified area */
containPixel(finder: any, value: number[]): boolean;
/** Get chart as data URL */
getDataURL(opts?: { type?: string; pixelRatio?: number; backgroundColor?: string; excludeComponents?: string[] }): string;
/** Get connected charts as data URL */
getConnectedDataURL(opts?: { type?: string; pixelRatio?: number; backgroundColor?: string; excludeComponents?: string[] }): string;
/** Append data to series */
appendData(opts: { seriesIndex: number; data: any[] }): void;
/** Clear chart */
clear(): void;
/** Check if chart is disposed */
isDisposed(): boolean;
/** Dispose chart instance */
dispose(): void;
/** Set chart option */
setOption(option: Option, updateOptions?: UpdateOptions): void;
/** Show loading animation */
showLoading(opts?: LoadingOptions): void;
/** Hide loading animation */
hideLoading(): void;
}Usage Examples:
<template>
<v-chart ref="chartRef" :option="option" />
<button @click="exportChart">Export PNG</button>
<button @click="refreshChart">Refresh</button>
</template>
<script setup>
import { ref } from "vue";
const chartRef = ref();
function exportChart() {
const dataURL = chartRef.value.getDataURL({
type: "png",
pixelRatio: 2,
backgroundColor: "#fff"
});
// Use dataURL for download or display
}
function refreshChart() {
chartRef.value.clear();
chartRef.value.setOption(newOption, { notMerge: true });
}
</script>For native DOM events on the chart container, use the native: prefix:
<template>
<v-chart
@native:click="handleNativeClick"
@native:focus="handleFocus"
@native:blur="handleBlur"
/>
</template>
<script setup>
function handleNativeClick(event) {
console.log("Native DOM click:", event.target);
}
function handleFocus(event) {
console.log("Chart container focused");
}
function handleBlur(event) {
console.log("Chart container blurred");
}
</script>When manualUpdate is true, the chart won't automatically react to option changes. Use setOption method for manual updates.
<template>
<v-chart
ref="chart"
:option="option"
:manual-update="true"
/>
<button @click="updateChart">Update Chart</button>
</template>
<script setup>
import { ref } from "vue";
const chart = ref();
const option = ref({ /* initial option */ });
function updateChart() {
const newOption = { /* updated option */ };
chart.value.setOption(newOption, { notMerge: true });
}
</script>Charts can be linked by setting the same group name, enabling coordinated interactions.
<template>
<v-chart :option="chart1Option" group="dashboard" />
<v-chart :option="chart2Option" group="dashboard" />
</template>
<script setup>
// Both charts will be linked for brush selection, data zoom, etc.
const chart1Option = ref({ /* ... */ });
const chart2Option = ref({ /* ... */ });
</script>Advanced loading state patterns:
<template>
<div class="chart-container">
<v-chart
ref="chartRef"
:option="chartOption"
:loading="isLoading"
:loading-options="loadingConfig"
@rendered="onChartRendered"
/>
<div v-if="error" class="error-message">
{{ error }}
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import VChart from "vue-echarts";
const chartRef = ref();
const chartOption = ref(null);
const isLoading = ref(true);
const error = ref(null);
const loadingConfig = {
text: "Loading chart data...",
color: "#409eff",
textColor: "#000",
maskColor: "rgba(255, 255, 255, 0.8)",
showSpinner: true,
spinnerRadius: 10
};
// Simulate data loading
async function loadChartData() {
try {
isLoading.value = true;
error.value = null;
const response = await fetch("/api/chart-data");
const data = await response.json();
chartOption.value = {
xAxis: { type: "category", data: data.categories },
yAxis: { type: "value" },
series: [{ type: "bar", data: data.values }]
};
} catch (err) {
error.value = "Failed to load chart data";
console.error(err);
} finally {
isLoading.value = false;
}
}
function onChartRendered() {
console.log("Chart rendered successfully");
}
// Load data on mount
loadChartData();
</script>Handling complex chart updates with animation:
<template>
<div>
<v-chart
ref="chart"
:option="chartOption"
:update-options="updateConfig"
/>
<button @click="addDataPoint">Add Data</button>
<button @click="changeChartType">Toggle Type</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const chart = ref();
const chartType = ref("bar");
const updateConfig = {
notMerge: false,
lazyUpdate: false,
silent: false
};
const chartOption = ref({
animation: true,
animationDuration: 1000,
xAxis: {
type: "category",
data: ["Jan", "Feb", "Mar", "Apr", "May"]
},
yAxis: { type: "value" },
series: [{
type: chartType.value,
data: [120, 200, 150, 80, 70],
animationDelay: (idx) => idx * 100
}]
});
function addDataPoint() {
const currentData = chartOption.value.series[0].data;
const months = chartOption.value.xAxis.data;
// Add new month and random data
const newMonth = `Month ${months.length + 1}`;
const newValue = Math.floor(Math.random() * 300) + 50;
chartOption.value = {
...chartOption.value,
xAxis: {
...chartOption.value.xAxis,
data: [...months, newMonth]
},
series: [{
...chartOption.value.series[0],
data: [...currentData, newValue]
}]
};
}
function changeChartType() {
chartType.value = chartType.value === "bar" ? "line" : "bar";
chartOption.value = {
...chartOption.value,
series: [{
...chartOption.value.series[0],
type: chartType.value
}]
};
}
</script>Install with Tessl CLI
npx tessl i tessl/npm-vue-echarts