How to Use Chart.js in a Nuxt 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.
Note
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.
# 👣 Steps
- Create a Nuxt project and install Chart.js and vue-chartjs.
npx create-nuxt-app nuxt-demo
cd nuxt-demo
npm i chart.js vue-chartjs
- Create a page for the chart, e.g. in
pages/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>
<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 incomponents
.
In 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 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 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
components
.
In components/ChartBar.vue
:
<template>
<b-card title="Bar">
<b-card img-bottom>
<ChartBarBase :chart-data="chartData" />
</b-card>
</b-card>
</template>
<script>
export default {
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 components/ChartDoughnut.vue
:
<template>
<b-card title="Doughnut">
<b-card img-bottom>
<ChartDoughnutBase :chart-data="chartData" />
</b-card>
</b-card>
</template>
<script>
export default {
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 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>
export default {
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.