I’ve been a big fan of the Chart.js library for years. It’s very easy to use for basic data visualization types, and there’s a variety of plugins that add more features and chart types.
A few weeks ago I learned about a library that sits right in the middle between the simplicity of Chart.js and the freedom to make any type of data visualization that a more complex library like D3.js offers: Apache ECharts.
ECharts also uses a configuration-based approach, much like Chart.js, which means, instead of writing code, as you would with D3.js, instead you configure your chart using various options and settings.
ECharts also supports a much larger variety of chart types, from basic bar and line charts, to calendars, pictorial charts, and 3D charts. And it also renders using SVG, as opposed to Chart.js only using the canvas
HTML element. (See the difference explained here.)
In this tutorial I will give you a quick overview of how the library works, and share an ECharts starter project made with Glitch that you can use to host your data visualization for free.
Introduction to Glitch¶
If you’re not familiar with Glitch, think of it as a code editor that comes with free hosting for basic static pages, with a paid upgrade for projects that require a backend.
The best part is that you don’t even need to create an account to try it out. If you like using it, feel free to do so, otherwise your app or your site will be automatically removed after some time.
Let’s get to it¶
Alright, with introductions out of the way, let’s create a remix of my starter template. Doing so will open a copy of my project which you can then edit as we go along.

All the files in this project are visible in the left-hand sidebar. Let me quickly explain how things are set up here.
Most of our work will be done in the data
and charts
folders. Inside data
, we will define the data for our chart. It might look like this:
export default [5, 20, 36, 10, 10, 20];
And then with a script inside the charts
folder, we will import this data and configure our chart.
import data from "../data/example-data.js";
export default {
aria: {
show: true,
},
title: {
text: "ECharts Getting Started Example",
padding: 20,
},
grid: {
top: 80,
bottom: 80,
containLabel: true,
},
legend: {
data: ["Sales"],
bottom: 20,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: ["Shirts", "Cardigans", "Chiffons", "Pants", "Heels", "Socks"],
},
yAxis: {},
series: [
{
name: "Sales",
type: "bar",
data,
},
],
};
This way we keep things nice and organized, and as I will show you later, we will also be able to reuse the same dataset in multiple charts.
Once we have both the data and the chart configuration defined, we will bring these together inside scripts/main.js
where we can do this:
/* import helper functions */
import onReady from "./modules/onReady.js";
import makeChart from "./modules/makeChart.js";
/* import chart options and data */
import exampleChart from "../charts/example-chart.js";
onReady(() => {
makeChart(document.getElementById("example-chart"), exampleChart);
});
This will turn our data into a chart using a placeholder element that’s set up inside index.html
.
<div id="example-chart" class="mt-5 w-100" style="min-height: 500px">
<!-- Your chart will be rendered here. -->
</div>
If you peak into the data
folder, you will see a file titled work-stoppages.js
. This is a dataset from U.S. Bureau of Labor Statistics showing a list times workers went on a strike, whether to demand better wages and working conditions, or some other reasons. (If this is a topic that interests you, you should also check out this map from Working Class History.)
I did the work of extracting the data for you, so that we can focus more on using ECharts.
Let’s start inside index.html
where we can replace the ID of placeholder chart. Let’s change example-chart
to work-stoppages
.
<div id="work-stoppages" class="mt-5 w-100" style="min-height: 500px">
<!-- Your chart will be rendered here. -->
</div>
Great. Now we can create a new script file charts/work-stoppages.js
. You can either use the plus sign next to “Files”, or duplicate the existing script file and renaming the copy.

First thing we will need to do inside charts/work-stoppages.js
is to import the work stoppage data from data/work-stoppages.js
.
import data from "../data/work-stoppages.js";
And export the chart configuration object. Here’s where we will have to do a bit of planning. Let’s have a closer at the data file.
export default [
{
year: 1947,
work_stopages: 270,
number_of_workers_thousands: 1629,
days_of_idleness_thousands: 25720,
},
{
year: 1948,
work_stopages: 245,
number_of_workers_thousands: 1435,
days_of_idleness_thousands: 26127,
},
{
year: 1949,
work_stopages: 262,
number_of_workers_thousands: 2537,
days_of_idleness_thousands: 43420,
},
/* ... */
We could create a table from this data that would look like this.
year | work_stopages | number_of_workers_thousands | days_of_idleness_thousands |
1947 | 270 | 1629 | 25720 |
1948 | 245 | 1435 | 26127 |
1949 | 262 | 2537 | 43420 |
We have to think about how we want to present this data. Let’s maybe start by plotting it all together in one line chart.
First, let’s split the columns into variables.
const workStoppageCount = data.map((row) => row.work_stopages);
const numberOfWorkers = data.map((row) => row.number_of_workers_thousands);
const daysOfIdleness = data.map((row) => row.days_of_idleness_thousands);
const years = data.map((row) => row.year);
Now we can use these in our configuration object.
export default {
aria: {
show: true,
},
title: {
text: "Work stoppages involving 1,000 or more workers, 1993 - present",
padding: 20,
},
grid: {
top: 80,
bottom: 80,
containLabel: true,
},
legend: {
data: ["Work stoppages", "Number of workers (thousands)", "Days of idleness (thousands)"],
bottom: 20,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: years,
},
yAxis: {},
series: [
{
stack: "main",
name: "Work stoppages",
type: "line",
data: workStoppageCount,
},
{
stack: "main",
name: "Number of workers (thousands)",
type: "line",
data: numberOfWorkers,
},
{
stack: "main",
name: "Days of idleness (thousands)",
type: "line",
data: daysOfIdleness,
},
],
};
Let me break down what’s going on here.
aria: {
show: true,
}
This setting tells ECharts to add a description to your chart for accessibility.
Title
should be pretty self-explanatory. Grid
lets you customize the area of the charts itself with options for padding, positioning, and more.
If we want the legend to show up, we will need to pass the names of our columns.
legend: {
data: ["Work stoppages", "Number of workers (thousands)", "Days of idleness (thousands)"],
bottom: 20,
}
These then need to match to what we use in our series
object.
series: [
{
stack: "main",
name: "Work stoppages",
type: "line",
data: workStoppageCount,
},
{
stack: "main",
name: "Number of workers (thousands)",
type: "line",
data: numberOfWorkers,
},
{
stack: "main",
name: "Days of idleness (thousands)",
type: "line",
data: daysOfIdleness,
},
]
Now we just have to go to scripts/main.js
and render our chart.
/* import helper functions */
import onReady from "./modules/onReady.js";
import makeChart from "./modules/makeChart.js";
/* import chart options and data */
import workStoppages from "../charts/work-stoppages.js";
onReady(() => {
makeChart(document.getElementById("work-stoppages"), workStoppages);
});
Let’s look at our result using the “Preview” button at the bottom of the page.

This is certainly a start. There’s a few ways we can make the chart easier to read. For this exercise, let’s just split the chart into three, showing each column of our dataset individually.
To do this, we just need to duplicate charts/work-stoppages.js
into two new files:
charts/work-stoppages.js
charts/work-stoppages-workers.js
charts/work-stoppages-days.js
And here’s how we can split up the configuration object.
// work-stoppages.js
import data from "../data/work-stoppages.js";
const workStoppageCount = data.map((row) => row.work_stopages);
const years = data.map((row) => row.year);
export default {
aria: {
show: true,
},
title: {
text: "Work stoppages involving 1,000 or more workers, 1993 - present",
padding: 20,
},
grid: {
top: 80,
bottom: 80,
containLabel: true,
},
legend: {
data: ["Work stoppages"],
bottom: 20,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: years,
},
yAxis: {},
series: [
{
stack: "main",
name: "Work stoppages",
type: "line",
data: workStoppageCount,
},
],
};
// charts/work-stoppages-workers.js
import data from "../data/work-stoppages.js";
const numberOfWorkers = data.map((row) => row.number_of_workers_thousands);
const years = data.map((row) => row.year);
export default {
aria: {
show: true,
},
title: {
text: "Work stoppages involving 1,000 or more workers, 1993 - present",
padding: 20,
},
grid: {
top: 80,
bottom: 80,
containLabel: true,
},
legend: {
data: ["Number of workers (thousands)"],
bottom: 20,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: years,
},
yAxis: {},
series: [
{
stack: "main",
name: "Number of workers (thousands)",
type: "line",
data: numberOfWorkers,
}
],
};
// charts/work-stoppages-days.js
import data from "../data/work-stoppages.js";
const daysOfIdleness = data.map((row) => row.days_of_idleness_thousands);
const years = data.map((row) => row.year);
export default {
aria: {
show: true,
},
title: {
text: "Work stoppages involving 1,000 or more workers, 1993 - present",
padding: 20,
},
grid: {
top: 80,
bottom: 80,
containLabel: true,
},
legend: {
data: ["Days of idleness (thousands)"],
bottom: 20,
},
tooltip: {
trigger: "axis",
},
xAxis: {
data: years,
},
yAxis: {},
series: [
{
stack: "main",
name: "Days of idleness (thousands)",
type: "line",
data: daysOfIdleness,
},
],
};
Let’s head back to index.html
. We will replace our one chart placeholder with three, and lay them out in columns using Bootstrap’s grid system.
<div class="row">
<div class="col-sm-12 col-md-4">
<div id="work-stoppages" class="mt-5 w-100" style="min-height: 500px">
<!-- Your chart will be rendered here. -->
</div>
</div>
<div class="col-sm-12 col-md-4">
<div id="work-stoppages-workers" class="mt-5 w-100" style="min-height: 500px">
<!-- Your chart will be rendered here. -->
</div>
</div>
<div class="col-sm-12 col-md-4">
<div id="work-stoppages-days" class="mt-5 w-100" style="min-height: 500px">
<!-- Your chart will be rendered here. -->
</div>
</div>
</div>
And now inside scripts/main.js
we will use these placeholders to render our separate charts.
/* import helper functions */
import onReady from "./modules/onReady.js";
import makeChart from "./modules/makeChart.js";
/* import chart options and data */
import workStoppages from "../charts/work-stoppages.js";
import workStoppagesWorkers from "../charts/work-stoppages-workers.js";
import workStoppagesDays from "../charts/work-stoppages-days.js";
onReady(() => {
makeChart(document.getElementById("work-stoppages"), workStoppages);
makeChart(document.getElementById("work-stoppages-workers"), workStoppagesWorkers );
makeChart(document.getElementById("work-stoppages-days"), workStoppagesDays );
});
Things are looking much better now! I went ahead and also moved the repeating title outside of each chart, and changed the colors. Here’s what I have now.

Now, here’s one more thing I’d like to do. When presenting data, I like to think about what it is that I’m trying to convey. I don’t really want to just say: here’s the data, look at it. Let’s see if we can tell a story here.
I’ll go ahead and create a new data file called data/federal-minimum-wage.js
using data from epi.org. Repeating the steps above, I can add a fourth chart right below the three we created together.

Adding a mark line and a mark area, I can highlight two things: that once the work stoppage activity peaked in 1974, the minimum wage started to stagnate. And that the minimum wage hasn’t changed since 2009.
I hope you found this tutorial useful, and until next time!