React Chart.js Data Labels

Table of Contents
Overview
This is a how-to for working with Chart.js. Chart.js is a great open source chart library downloaded over 300k times per week as of April 2022. This post will go over how to display a data label on a stacked bar chart with the chartjs-plugin-datalabels library. This plugin can be applied to a pie, donut, or any chart with a shaded area.
Source Code
Please get the source files for this demo.
GitHub RepoPrerequisites
New React App
Create a new Next.js application
npx create-next-app@latest
Chart.JS version 3.6.0
Chart.JS and React Chart.JS a wrapper for Chart.JS.
npm i chart.js react-chartjs-2
Chart.JS Data Labels Plugin version 2.0.0
chartjs-plugin-datalabels is a plugin for Chart.JS.
npm i chartjs-plugin-datalabels
Material – (Optional) version 5.4.3
MUI – Material UI
Material is only used for the table and layout of the demo.
npm install @mui/material @mui/lab @emotion/react @emotion/styled
Data for the example
Raw Data for the Chart and Table
Raw data for the Chart Component. You can put this in a separate file and export it or just drop it in the Parent Component App.js.
const rawData = [
{id: 1, day: 1, type: 1, airline: 'Southwest', passengers: 162, parkedHours: 24},
{id: 2, day: 1, type: 1, airline: 'Southwest', passengers: 208, parkedHours: 8},
{id: 3, day: 2, type: 1, airline: 'Southwest', passengers: 176, parkedHours: 16},
{id: 4, day: 2, type: 1, airline: 'Southwest', passengers: 204, parkedHours: 24},
{id: 5, day: 3, type: 1, airline: 'Southwest', passengers: 158, parkedHours: 58},
{id: 6, day: 4, type: 1, airline: 'Southwest', passengers: 202, parkedHours: 22},
{id: 7, day: 0, type: 1, airline: 'Southwest', passengers: 218, parkedHours: 38},
{id: 8, day: 4, type: 1, airline: 'Southwest', passengers: 136, parkedHours: 6},
{id: 9, day: 3, type: 1, airline: 'Southwest', passengers: 124, parkedHours: 24},
{id: 10, day: 3, type: 1, airline: 'Southwest', passengers: 158, parkedHours: 2},
{id: 11, day: 1, type: 1, airline: 'American', passengers: 162, parkedHours: 24},
{id: 12, day: 1, type: 1, airline: 'American', passengers: 208, parkedHours: 8},
{id: 13, day: 2, type: 1, airline: 'American', passengers: 176, parkedHours: 16},
{id: 14, day: 2, type: 1, airline: 'American', passengers: 204, parkedHours: 24},
{id: 15, day: 3, type: 1, airline: 'American', passengers: 158, parkedHours: 58},
{id: 16, day: 4, type: 1, airline: 'American', passengers: 202, parkedHours: 22},
{id: 17, day: 0, type: 1, airline: 'American', passengers: 218, parkedHours: 38},
{id: 18, day: 4, type: 1, airline: 'American', passengers: 136, parkedHours: 6},
{id: 19, day: 3, type: 1, airline: 'American', passengers: 124, parkedHours: 24},
{id: 20, day: 3, type: 1, airline: 'American', passengers: 158, parkedHours: 2},
{id: 21, day: 1, type: 1, airline: 'Allegiant', passengers: 162, parkedHours: 24},
{id: 22, day: 1, type: 1, airline: 'Allegiant', passengers: 208, parkedHours: 8},
{id: 23, day: 2, type: 1, airline: 'Allegiant', passengers: 176, parkedHours: 16},
{id: 24, day: 2, type: 1, airline: 'Allegiant', passengers: 204, parkedHours: 24},
{id: 25, day: 3, type: 1, airline: 'Allegiant', passengers: 158, parkedHours: 58},
{id: 26, day: 4, type: 1, airline: 'Allegiant', passengers: 202, parkedHours: 22},
{id: 27, day: 0, type: 1, airline: 'Allegiant', passengers: 218, parkedHours: 38},
{id: 28, day: 4, type: 1, airline: 'Allegiant', passengers: 136, parkedHours: 6},
{id: 29, day: 3, type: 1, airline: 'Allegiant', passengers: 124, parkedHours: 24},
{id: 30, day: 3, type: 1, airline: 'Allegiant', passengers: 158, parkedHours: 2},
{id: 31, day: 1, type: 2, airline: 'Capital One', passengers: 12, parkedHours: 4},
{id: 32, day: 1, type: 2, airline: 'Bank of America', passengers: 28, parkedHours: 8},
{id: 33, day: 2, type: 2, airline: 'Charles Schwab', passengers: 16, parkedHours: 6},
{id: 34, day: 2, type: 2, airline: 'Iron Maiden Corp', passengers: 24, parkedHours: 24},
{id: 35, day: 3, type: 2, airline: 'Scorpions Corp', passengers: 18, parkedHours: 8},
{id: 36, day: 4, type: 2, airline: 'Fed Ex', passengers: 22, parkedHours: 2},
{id: 37, day: 0, type: 2, airline: 'Amazon', passengers: 8, parkedHours: 8},
{id: 38, day: 4, type: 2, airline: 'M Hotel', passengers: 16, parkedHours: 6},
{id: 39, day: 3, type: 2, airline: 'Wells Corp', passengers: 4, parkedHours: 24},
{id: 40, day: 3, type: 2, airline: 'Smith Corp', passengers: 18, parkedHours: 2},
{id: 41, day: 3, type: 2, airline: 'Walton Corp', passengers: 8, parkedHours: 5},
];
Randomly Generated Chart Data
Create an JSON array for the chart by getting the daily counts for type 1 & 2 flights. This fictitious data has flights for Monday to Friday which is put into an array. Values for days Monday = 1, Tuesday = 2 etc.
The chart data will have {label : day, CFCount: }
useEffect(() => {
setIsLoaded(false)
const day = ['Mon', 'Tues', 'Wed', 'Thu', 'Fri']
var chartData = []
for(let i=0; i <= 5; i++) {
const cfCnt = rawData.filter(c => c.day === i && c.type === 1).length
const gaCnt = rawData.filter(c => c.day === i && c.type === 2).length
chartData.push({label: day[i], commercialCount: cfCnt, generalCount: gaCnt})
}
setChartData(chartData)
setTimeout(() => {
setIsLoaded(true)
}, 100);
}, []);
Create the Chart Component
Under the component folder create a new file “componentsChartComp.js“.
import React, { useRef } from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import {
Bar,
getElementAtEvent,
} from 'react-chartjs-2';
import ChartDataLabels from 'chartjs-plugin-datalabels';
Register the Chart.JS and Data Label Components
Remember, every single component from chart.js import your chart will use must be registered.
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
ChartDataLabels,
Title,
Tooltip,
Legend
);
Enable the Data Label Plugin

To enable a stacked bar chart, set stacked to true under options -> scales -> x & y.
The data labels must be set in two areas, the options and dataset
Set the “options -> plugins -> dataLabels: { display: true }” and then “dataset -> dataLabel -> color“. In this example the color is white for max contrast.
export default function ChartComp(props) {
const options = {
plugins: {
datalabels: {
display: true,
},
title: {
display: true,
text: '- Stacked',
},
},
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
},
},
};
const labels= props.data.map(c => c.label);
const data = {
labels,
datasets: [
{
label: 'Commercial Flights',
data: props.data.map(c => c.commercialCount),
backgroundColor: '#58508d',
datalabels: {
color: 'white'
},
},
{
label: 'General Aviation',
data: props.data.map(c => c.generalCount),
backgroundColor: '#ff6361',
datalabels: {
color: 'white'
},
},
],
tooltips: {
},
};
Render the Bar Chart
return <Bar options={options} data={data} />
How to Generate Chart Data from Raw Data
From my experience building charts for custom web applications, the raw API data must be modified to work with a chart. In the example below, each day will count Type 1 or 2.
Loop the raw data to create a JSON array by getting the daily counts for Type 1 & 2 flights using the JavaScript filter() function. This fake data has flights for Monday to Friday using values 1 to 5 from an array. Values for days Monday = 1, Tuesday = 2 etc.
rawData.filter(c => c.day === i && c.type === 1).length
The chart data will have {label : day[i], commercialCount: cfCnt, generalCount: gaCnt: } pushed into an array.
const [ chartData, setChartData ] = useState([])
const [ isLoaded, setIsLoaded ] = useState(false);
...
useEffect(() => {
setIsLoaded(false)
const day = ['Mon', 'Tues', 'Wed', 'Thu', 'Fri']
var chartData = []
for(let i=0; i <= 5; i++) {
const cfCnt = rawData.filter(c => c.day === i && c.type === 1).length
const gaCnt = rawData.filter(c => c.day === i && c.type === 2).length
chartData.push({label: day[i], commercialCount: cfCnt, generalCount: gaCnt})
}
setChartData(chartData)
setTimeout(() => {
setIsLoaded(true)
}, 100);
}, []);
The whole App.js component.
import { useState, useEffect } from 'react';
import ChartComp from './components/ChartComp'
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
const rawData = [
{id: 1, day: 1, type: 1, airline: 'Southwest', passengers: 162, parkedHours: 24},
{id: 2, day: 1, type: 1, airline: 'Southwest', passengers: 208, parkedHours: 8},
{id: 3, day: 2, type: 1, airline: 'Southwest', passengers: 176, parkedHours: 16},
{id: 4, day: 2, type: 1, airline: 'Southwest', passengers: 204, parkedHours: 24},
{id: 5, day: 3, type: 1, airline: 'Southwest', passengers: 158, parkedHours: 58},
{id: 6, day: 4, type: 1, airline: 'Southwest', passengers: 202, parkedHours: 22},
{id: 7, day: 0, type: 1, airline: 'Southwest', passengers: 218, parkedHours: 38},
{id: 8, day: 4, type: 1, airline: 'Southwest', passengers: 136, parkedHours: 6},
{id: 9, day: 3, type: 1, airline: 'Southwest', passengers: 124, parkedHours: 24},
{id: 10, day: 3, type: 1, airline: 'Southwest', passengers: 158, parkedHours: 2},
{id: 11, day: 1, type: 1, airline: 'American', passengers: 162, parkedHours: 24},
{id: 12, day: 1, type: 1, airline: 'American', passengers: 208, parkedHours: 8},
{id: 13, day: 2, type: 1, airline: 'American', passengers: 176, parkedHours: 16},
{id: 14, day: 2, type: 1, airline: 'American', passengers: 204, parkedHours: 24},
{id: 15, day: 3, type: 1, airline: 'American', passengers: 158, parkedHours: 58},
{id: 16, day: 4, type: 1, airline: 'American', passengers: 202, parkedHours: 22},
{id: 17, day: 0, type: 1, airline: 'American', passengers: 218, parkedHours: 38},
{id: 18, day: 4, type: 1, airline: 'American', passengers: 136, parkedHours: 6},
{id: 19, day: 3, type: 1, airline: 'American', passengers: 124, parkedHours: 24},
{id: 20, day: 3, type: 1, airline: 'American', passengers: 158, parkedHours: 2},
{id: 21, day: 1, type: 1, airline: 'Allegiant', passengers: 162, parkedHours: 24},
{id: 22, day: 1, type: 1, airline: 'Allegiant', passengers: 208, parkedHours: 8},
{id: 23, day: 2, type: 1, airline: 'Allegiant', passengers: 176, parkedHours: 16},
{id: 24, day: 2, type: 1, airline: 'Allegiant', passengers: 204, parkedHours: 24},
{id: 25, day: 3, type: 1, airline: 'Allegiant', passengers: 158, parkedHours: 58},
{id: 26, day: 4, type: 1, airline: 'Allegiant', passengers: 202, parkedHours: 22},
{id: 27, day: 0, type: 1, airline: 'Allegiant', passengers: 218, parkedHours: 38},
{id: 28, day: 4, type: 1, airline: 'Allegiant', passengers: 136, parkedHours: 6},
{id: 29, day: 3, type: 1, airline: 'Allegiant', passengers: 124, parkedHours: 24},
{id: 30, day: 3, type: 1, airline: 'Allegiant', passengers: 158, parkedHours: 2},
{id: 31, day: 1, type: 2, airline: 'Capital One', passengers: 12, parkedHours: 4},
{id: 32, day: 1, type: 2, airline: 'Bank of America', passengers: 28, parkedHours: 8},
{id: 33, day: 2, type: 2, airline: 'Charles Schwab', passengers: 16, parkedHours: 6},
{id: 34, day: 2, type: 2, airline: 'Iron Maiden Corp', passengers: 24, parkedHours: 24},
{id: 35, day: 3, type: 2, airline: 'Scorpions Corp', passengers: 18, parkedHours: 8},
{id: 36, day: 4, type: 2, airline: 'Fed Ex', passengers: 22, parkedHours: 2},
{id: 37, day: 0, type: 2, airline: 'Amazon', passengers: 8, parkedHours: 8},
{id: 38, day: 4, type: 2, airline: 'M Hotel', passengers: 16, parkedHours: 6},
{id: 39, day: 3, type: 2, airline: 'Wells Corp', passengers: 4, parkedHours: 24},
{id: 40, day: 3, type: 2, airline: 'Smith Corp', passengers: 18, parkedHours: 2},
{id: 41, day: 3, type: 2, airline: 'Walton Corp', passengers: 8, parkedHours: 5},
];
export default function App() {
const [ chartData, setChartData ] = useState([])
const [ tableData, setTableData ] = useState([])
const [ isLoaded, setIsLoaded ] = useState(false);
useEffect(() => {
setIsLoaded(false)
const day = ['Mon', 'Tues', 'Wed', 'Thu', 'Fri']
var chartData = []
for(let i=0; i <= 5; i++) {
const cfCnt = rawData.filter(c => c.day === i && c.type === 1).length
const gaCnt = rawData.filter(c => c.day === i && c.type === 2).length
chartData.push({label: day[i], commercialCount: cfCnt, generalCount: gaCnt})
}
setChartData(chartData)
setTimeout(() => {
setIsLoaded(true)
}, 100);
}, []);
const onHandleBarClickEvent = (barIndex, stackIndex) => {
if(stackIndex === 0) {
setTableData(rawData.filter(c => c.day === barIndex && c.type === 1))
}
else {
setTableData(rawData.filter(c => c.day === barIndex && c.type === 2))
}
}
return (
<div className="App">
<Box sx={{ flexGrow: 1, marginTop: 12 }}>
<Grid container spacing={2}>
<Grid item xs={12} md={2}></Grid>
<Grid item xs={12} md={8}>
<ChartComp data={chartData}
onHandleBarClickEvent={onHandleBarClickEvent}/>
</Grid>
</Grid>
</Box>
</div>
);
}
You must be logged in to post a comment.