Designing Multi–Y-Axis Charts in ECharts with Bars, Lines, and Mixed Series
Designing multi–Y-axis charts in ECharts becomes essential when visualizing measures at different scales (for example, thousands of devices vs. tens of products). The following patterns progressively build from a single axis to several Y-axes and finallly to mixed-series charts.
Setup
- Include ECharts and prepare a container sized explicitly with width and height.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Multi-Y Axis with ECharts</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<style>
#chart { width: 1200px; height: 500px; }
</style>
</head>
<body>
<div id="chart"></div>
<script>
const months = [
'2016-01','2016-02','2016-03','2016-04','2016-05','2016-06',
'2016-07','2016-08','2016-09','2016-10','2016-11','2016-12'
];
const dataDevices = [9200, 1800, 1400, 3100, 6200, 9800, 2300, 3400, 3720, 5300, 1600, 8700];
const dataProducts = [12, 44, 85, 31, 49, 90, 36, 22, 48, 110, 27, 160];
const dataVendors = [8, 12, 22, 28, 18, 15, 14, 40, 55, 63, 88, 120];
const dataUsers = [25000, 6200, 47000, 12000, 28000, 19000, 60000, 42000, 44000, 70000, 89000, 15000];
const dataMetricX = [120000, 40000, 240000, 100000, 230000, 150000, 90000, 470000, 130000, 560000, 280000, 140000];
const palette = ['#5470C6', '#91CC75', '#EE6666', '#73C0DE', '#FAC858'];
const chart = echarts.init(document.getElementById('chart'));
function render(option) {
chart.clear();
chart.setOption(option, true);
}
</script>
</body>
</html>
Single Y-axis, single series A basic column chart with one Y-axis and one series.
<script>
const singleAxisOption = {
color: [palette[0]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 20, top: 30, bottom: 40 },
legend: { data: ['Devices Added'] },
xAxis: {
type: 'category',
data: months,
axisTick: { alignWithLabel: true }
},
yAxis: {
type: 'value',
name: 'Devices',
min: 0, max: 11000,
axisLabel: { formatter: (v) => v }
},
series: [
{ name: 'Devices Added', type: 'bar', barMaxWidth: 30, data: dataDevices }
]
};
render(singleAxisOption);
</script>
Single Y-axis, two series (scale mismatch) Adding a second measure of a much smaller magnitude makes the smaller series hard to read when sharing a single Y-axis.
<script>
const singleAxisTwoSeriesOption = {
color: [palette[0], palette[1]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 20, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added'] },
xAxis: { type: 'category', data: months },
yAxis: { type: 'value', name: 'Count' },
series: [
{ name: 'Devices Added', type: 'bar', data: dataDevices },
{ name: 'Products Added', type: 'bar', data: dataProducts }
]
};
render(singleAxisTwoSeriesOption);
</script>
Dual Y-axes Map each series to its own axis using yAxisIndex. Place one axis on the left and one on the right.
<script>
const twoAxesOption = {
color: [palette[0], palette[1]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 60, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } }
],
series: [
{ name: 'Devices Added', type: 'bar', barMaxWidth: 30, data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'bar', barMaxWidth: 30, data: dataProducts, yAxisIndex: 1 }
]
};
render(twoAxesOption);
</script>
Three Y-axes Add a third axis on the right and push it away using offset to avoid overlap.
<script>
const threeAxesOption = {
color: [palette[0], palette[1], palette[2]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 120, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added', 'Vendors Onboarded'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } },
{ type: 'value', name: 'Vendors', position: 'right', offset: 70, min: 0, max: 200, axisLine: { lineStyle: { color: palette[2] } } }
],
series: [
{ name: 'Devices Added', type: 'bar', data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'bar', data: dataProducts, yAxisIndex: 1 },
{ name: 'Vendors Onboarded', type: 'bar', data: dataVendors, yAxisIndex: 2 }
]
};
render(threeAxesOption);
</script>
Four Y-axes Introduce a fourth measure with a much larger scale (e.g., new users). Increase right grid spacing and manage offsets.
<script>
const fourAxesOption = {
color: [palette[0], palette[1], palette[2], palette[3]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 200, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added', 'Vendors Onboarded', 'Users Joined'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } },
{ type: 'value', name: 'Vendors', position: 'right', offset: 70, min: 0, max: 200, axisLine: { lineStyle: { color: palette[2] } } },
{ type: 'value', name: 'Users', position: 'right', offset: 150, min: 0, max: 90000, axisLine: { lineStyle: { color: palette[3] } } }
],
series: [
{ name: 'Devices Added', type: 'bar', data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'bar', data: dataProducts, yAxisIndex: 1 },
{ name: 'Vendors Onboarded', type: 'bar', data: dataVendors, yAxisIndex: 2 },
{ name: 'Users Joined', type: 'bar', data: dataUsers, yAxisIndex: 3 }
]
};
render(fourAxesOption);
</script>
Five Y-axes Add a fifth axis for an even larger metric (e.g., revenue), with an additional offset.
<script>
const fiveAxesOption = {
color: [palette[0], palette[1], palette[2], palette[3], palette[4]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 260, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added', 'Vendors Onboarded', 'Users Joined', 'Metric X'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } },
{ type: 'value', name: 'Vendors', position: 'right', offset: 70, min: 0, max: 200, axisLine: { lineStyle: { color: palette[2] } } },
{ type: 'value', name: 'Users', position: 'right', offset: 150, min: 0, max: 90000, axisLine: { lineStyle: { color: palette[3] } } },
{ type: 'value', name: 'Metric X', position: 'right', offset: 220, min: 0, max: 600000, axisLine: { lineStyle: { color: palette[4] } } }
],
series: [
{ name: 'Devices Added', type: 'bar', data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'bar', data: dataProducts, yAxisIndex: 1 },
{ name: 'Vendors Onboarded', type: 'bar', data: dataVendors, yAxisIndex: 2 },
{ name: 'Users Joined', type: 'bar', data: dataUsers, yAxisIndex: 3 },
{ name: 'Metric X', type: 'bar', data: dataMetricX, yAxisIndex: 4 }
]
};
render(fiveAxesOption);
</script>
All-line variant (multi–Y-axis) Switch all series to lines by changing series.type to 'line'.
<script>
const fiveAxesLinesOption = {
color: [palette[0], palette[1], palette[2], palette[3], palette[4]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 260, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added', 'Vendors Onboarded', 'Users Joined', 'Metric X'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } },
{ type: 'value', name: 'Vendors', position: 'right', offset: 70, min: 0, max: 200, axisLine: { lineStyle: { color: palette[2] } } },
{ type: 'value', name: 'Users', position: 'right', offset: 150, min: 0, max: 90000, axisLine: { lineStyle: { color: palette[3] } } },
{ type: 'value', name: 'Metric X', position: 'right', offset: 220, min: 0, max: 600000, axisLine: { lineStyle: { color: palette[4] } } }
],
series: [
{ name: 'Devices Added', type: 'line', smooth: true, data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'line', smooth: true, data: dataProducts, yAxisIndex: 1 },
{ name: 'Vendors Onboarded', type: 'line', smooth: true, data: dataVendors, yAxisIndex: 2 },
{ name: 'Users Joined', type: 'line', smooth: true, data: dataUsers, yAxisIndex: 3 },
{ name: 'Metric X', type: 'line', smooth: true, data: dataMetricX, yAxisIndex: 4 }
]
};
render(fiveAxesLinesOption);
</script>
Mixed bars and lines (multi–Y-axis) Mixing series types (some bars, some lines) highlights different dynamics while keeping separate axes for scale.
<script>
const mixedOption = {
color: [palette[0], palette[1], palette[2], palette[3], palette[4]],
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 260, top: 30, bottom: 40 },
legend: { data: ['Devices Added', 'Products Added', 'Vendors Onboarded', 'Users Joined', 'Metric X'] },
xAxis: { type: 'category', data: months },
yAxis: [
{ type: 'value', name: 'Devices', position: 'left', min: 0, max: 11000, axisLine: { lineStyle: { color: palette[0] } } },
{ type: 'value', name: 'Products', position: 'right', min: 0, max: 200, axisLine: { lineStyle: { color: palette[1] } } },
{ type: 'value', name: 'Vendors', position: 'right', offset: 70, min: 0, max: 200, axisLine: { lineStyle: { color: palette[2] } } },
{ type: 'value', name: 'Users', position: 'right', offset: 150, min: 0, max: 90000, axisLine: { lineStyle: { color: palette[3] } } },
{ type: 'value', name: 'Metric X', position: 'right', offset: 220, min: 0, max: 600000, axisLine: { lineStyle: { color: palette[4] } } }
],
series: [
{ name: 'Devices Added', type: 'line', smooth: true, data: dataDevices, yAxisIndex: 0 },
{ name: 'Products Added', type: 'line', smooth: true, data: dataProducts, yAxisIndex: 1 },
{ name: 'Vendors Onboarded', type: 'bar', barMaxWidth: 28, data: dataVendors, yAxisIndex: 2 },
{ name: 'Users Joined', type: 'bar', barMaxWidth: 28, data: dataUsers, yAxisIndex: 3 },
{ name: 'Metric X', type: 'bar', barMaxWidth: 28, data: dataMetricX, yAxisIndex: 4 }
]
};
render(mixedOption);
</script>
Implementation notes
- Add a new Y-axis by appending an entry to the yAxis array; place additional right-side axes using offset to prevent overlap.
- Bind each series to its axis with yAxisIndex.
- Tune grid.right to leave space for multiple right-side axes.
- Optionally color-code axisLine.lineStyle.color to match series for readability.
- Choose bar or line series types to emphasize distribution or trend.