How to Use Chart.js in a Vue Project demo repo
Chart.js (opens new window) is a simple HTML5 Charts using the <canvas>
tag.
vue-chartjs (opens new window) is a wrapper for Chart.js in Vue.
Before, I used vue-charts (opens new window) as the wrapper. But since it is now deprecated, I use another wrapper and updated this page.
WARNING
vue-chartjs is not compatible with Vue 3 at the time of this writing, so this tutorial is only for Vue 2.
# 👣 Steps
- Create a Vue project and install Chart.js and vue-chartjs.
npm i -g @vue/cli
vue create vue2-demo
cd vue2-demo
npm i chart.js vue-chartjs
- Create a page for the chart, e.g. in
src/views/chart.vue
. Here I will create bar, doughnut, and line charts.
<template>
<div>
<h1>Chart Demo</h1>
<div class="grid">
<ChartBar />
<ChartDoughnut />
<ChartLine />
</div>
</div>
</template>
<script>
import ChartBar from "@/components/ChartBar";
import ChartDoughnut from "@/components/ChartDoughnut";
import ChartLine from "@/components/ChartLine";
export default {
components: {
ChartBar,
ChartDoughnut,
ChartLine
}
};
</script>
<style lang="scss" scoped>
.grid {
display: grid;
gap: 2rem;
@media (min-width: 768px) {
grid-template-columns: 1fr 1fr;
}
}
</style>
- Create a
.js
components for the base charts insrc/components
.
In src/components/ChartBarBase.js
:
import { Bar, mixins } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Bar,
mixins: [reactiveProp],
props: ["chartData"],
data() {
return {
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
responsive: true
}
};
},
mounted() {
this.renderChart(this.chartData, this.options);
}
};
In src/components/ChartDoughnutBase.js
:
import { Doughnut, mixins } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Doughnut,
mixins: [reactiveProp],
props: ["chartData"],
data() {
return {
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
responsive: true
}
};
},
mounted() {
this.renderChart(this.chartData, this.options);
}
};
In src/components/ChartLineBase.js
:
import { Line, mixins } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
extends: Line,
mixins: [reactiveProp],
props: ["chartData"],
data() {
return {
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
},
responsive: true
}
};
},
mounted() {
this.renderChart(this.chartData, this.options);
}
};
- Create the wrapper component for each chart in
src/components
.
In src/components/ChartBar.vue
:
<template>
<b-card title="Bar">
<b-card img-bottom>
<ChartBarBase :chart-data="chartData" />
</b-card>
</b-card>
</template>
<script>
import ChartBarBase from "@/components/ChartBarBase";
export default {
components: {
ChartBarBase
},
data() {
return {
chartData: null
};
},
mounted() {
this.fillData();
},
methods: {
fillData() {
this.chartData = {
labels: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
datasets: [
{
backgroundColor: "#de98ab",
borderColor: "#0c0306",
data: [1, 3, 5, 7, 2, 4, 6],
label: "Bar"
},
{
backgroundColor: "#98ddde",
borderColor: "#030c0c",
data: [1, 5, 2, 6, 3, 7, 4],
label: "Baz"
}
]
};
}
}
};
</script>
In src/components/ChartDoughnut.vue
:
<template>
<b-card title="Doughnut">
<b-card img-bottom>
<ChartDoughnutBase :chart-data="chartData" />
</b-card>
</b-card>
</template>
<script>
import ChartDoughnutBase from "@/components/ChartDoughnutBase";
export default {
components: {
ChartDoughnutBase
},
data() {
return {
chartData: null
};
},
mounted() {
this.fillData();
},
methods: {
fillData() {
this.chartData = {
labels: ["Foo", "Bar", "Baz"],
datasets: [
{
backgroundColor: ["#f36e60", "#ffdb3b", "#185190"],
hoverBackgroundColor: ["#fbd2cd", "#fef5c9", "#d1e3f7"],
data: [10, 20, 40]
}
]
};
}
}
};
</script>
In src/components/ChartLine.vue
:
<template>
<b-card title="Line">
<b-form-group>
<b-form-radio
v-for="(item, index) in btn"
:key="index"
v-model="radio"
:name="item.label"
:value="item.value"
@change="updateChart"
>
{{ item.label }}
</b-form-radio>
</b-form-group>
<b-card img-bottom>
<ChartLineBase :chart-data="chartData" />
</b-card>
</b-card>
</template>
<script>
import ChartLineBase from "@/components/ChartLineBase";
export default {
components: {
ChartLineBase
},
data() {
return {
btn: [
{ label: "Today", value: "day" },
{ label: "This Week", value: "week" }
],
chartData: null,
data: {
day: [1, 3, 5, 3, 1],
week: [12, 14, 16, 18, 11, 13, 15]
},
labels: {
day: [8, 10, 12, 14, 16],
week: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
},
radio: "day"
};
},
mounted() {
this.fillData();
},
methods: {
fillData() {
this.chartData = {
labels: this.labels[this.radio],
datasets: [
{
borderColor: "#81894e",
data: this.data[this.radio],
label: "Foo"
}
]
};
},
updateChart() {
this.$nextTick(() => {
this.fillData();
});
}
}
};
</script>
# 📖 Explanation
# Base
extends
extends the imported base chart.reactiveProp
watches changes to the prop, so the chart can update or re-render if the data has changed.props
registers the prop for passing data to the chart base.options
is for storing options that will be used by the respective chart. You can pass them as a prop in each chart component instead if you need to use unique options in every chart.beginAtZero
makes the chart starts from zero if set totrue
.responsive
makes the chart responsive if set totrue
.
- Calling
this.renderChart()
creates the chart instance.
INFO
Separating the base from the wrapper component makes the component reusable because you can use one base for multiple charts with the same type but with different data. But if you only need one chart, you can input the data directly inside the base component. Not that I recommend it.
# Bar
chartData
is initiallynull
. Aftermounted
, thefillData
method is called and fill the data.labels
sets the x-axis labels.datasets
is an array for dataset objects.backgroundcolor
changes the background color.bordercolor
specifies the border color.data
accepts an array of data.label
is for the data name.
# Doughnut
hoverBackgroundColor
is for setting the background color when it is hovered over.
# Line
updateChart
is called instead offillData
because@change
can precedev-model
, meaning the chart sees the radio value just before it changes, making it show the wrong data. I heard it is different for different browsers, but it happens in Firefox. To be safe, just wait for the next tick before re-filling the data.