This is a continuation of Part 1 of our blog series. It's advisable to read Part 1 to understand how we got to this stage.
For reference, here's the outline of this blog series:
In this, we'll learn how to visualize the financial data we dealt with in the previous part using Chart.js. We want to view the financial data of our Finance Tracker app using bar charts and pie charts.
Why is it important to visualize financial data? Visualizing financial data is essential for several reasons such as:
Chart.js is a flexible and easy-to-use open-source JavaScript library for creating charts and graphs. It is easy to use and provides a simple API for creating different types of charts with a few lines of code.
Chart.js offers customization options, allowing users to style charts to meet their specific requirements. It also has built-in support for animations.
To visualize our application's budget data, we'll use two charts (a bar chart and a pie chart).
npm install chart.js
chartjs-plugin-datalabels
plugin. This plugin allows us to display custom labels inside or outside chart elements, like pie slices. We'll be using it specifically for the pie chart.npm install chartjs-plugin-datalabels
budget
folder, located inside the dashboard
folder. We'll name them - BarChart.tsx
and PieChart.tsx
. Overview.tsx
Component for Receiving Chart DataIf you recall, or you can refer back to Part 1 of this series, you'll see how the Overview
component is structured. We'll need to add a few things to implement this visualization feature.
Let's modify our Overview
component to fetch the budget data from Strapi CMS and display the charts.
Here's how we'll do it:
First, we'll import the hooks we'll be using. We'll import the Axios library and then the two newly created components in our Overview.tsx component.
1"use client";
2import React, { useState, useEffect } from "react";
3import axios from "axios";
4import BarChart from "./budget/BarChart";
5import PieChart from "./budget/PieChart";
1const [budgets, setBudgets] = useState<{ category: string; amount: number }[]>(
2 [],
3);
4const [chartType, setChartType] = useState<"bar" | "pie">("bar");
useEffect
hook to take two arguments. The first argument is a function that runs as the side effect and an empty dependency array. This dependency array will only run once when the component mounts.1useEffect(() => {
2 ...
3}, []);
useEffect
hook, we'll create a fetchBudgets
asynchronous function to fetch the budget data from the API. Using the Axios
library, we'll send a GET
request to the budget API endpoint. res
will contain the data
property of the budget data. Then, we'll use the map
array method to iterate over each budget item and create a new array of objects containing only the category and amount properties.setBudgets(data)
will then update the budget state with the fetched and mapped data.1const fetchBudgets = async () => {
2 try {
3 const res = await axios.get(
4 "http://localhost:1337/api/budgets?populate=budget",
5 );
6 const data = res.data.data.map((budget: any) => ({
7 category: budget.attributes.category,
8 amount: budget.attributes.amount,
9 }));
10 setBudgets(data);
11 } catch (error) {
12 console.error("Error fetching budgets:", error);
13 }
14};
15
16fetchBudgets();
fetchBudgets
function after defining it within the useEffect
hook.categories
and amounts
:1const categories = budgets.map((budget) => budget.category);
2const amounts = budgets.map((budget) => budget.amount);
It's time to write the functionalities to create these charts.
We will navigate to the BarChart.tsx
component.
chart.js
library. We'll use the auto
package to ensure all features from chart.js
are available.1import React, { useEffect, useRef } from "react";
2import Chart from "chart.js/auto";
categories
and amounts
.1interface BarChartProps {
2 categories: string[];
3 amounts: number[];
4}
BarChart
component will take categories
and amounts
as props. We will create a chartRef
function to reference the canvas element, which we will soon create to represent the Chart.js instance.1const BarChart: React.FC<BarChartProps> = ({ categories, amounts }) => {
2const chartRef = useRef<HTMLCanvasElement | null>(null);
useEffect
hook, we'll create the chart when the component mounts or when the categories and amounts props change:1useEffect(() => {
2 if (chartRef.current) {
3 const chartInstance = new Chart(chartRef.current, {
4 type: "bar",
5 data: {
6 labels: categories,
7 datasets: [
8 {
9 label: "Budget Amount",
10 data: amounts,
11 backgroundColor: [
12 "rgba(75, 192, 192, 0.2)",
13 "rgba(54, 162, 235, 0.2)",
14 "rgba(153, 102, 255, 0.2)",
15 ],
16 borderColor: [
17 "rgb(75, 192, 192)",
18 "rgb(54, 162, 235)",
19 "rgb(153, 102, 255)",
20 ],
21 borderWidth: 1,
22 },
23 ],
24 },
25 options: {
26 scales: {
27 y: {
28 beginAtZero: true,
29 },
30 },
31 plugins: {
32 title: {
33 display: true,
34 text: "Budget data - bar chart",
35 padding: {
36 top: 10,
37 bottom: 30,
38 },
39 },
40 },
41 },
42 });
43
44 return () => {
45 chartInstance.destroy();
46 };
47 }
48}, [categories, amounts]);
49
50return <canvas ref={chartRef} />;
In the useEffect
function above, we added the if (chartRef.current)
to ensure that the chart is only created if the canvas element is available.
The new Chart(chartRef.current, { ... })
creates a new Chart.js instance with the specified type, data, and options.
type: 'bar'
specifies that the chart is a bar chart.
The data
object contains the chart's labels (categories
) and datasets (amounts
), including the label, data, and bar colors.
The options
object is the configuration options for the chart, such as making the y-axis start at zero. The plugins
object inside the `options has a property for setting a title for the chart.
The return () => { chartInstance.destroy(); }
statement cleans up the chart instance when the component unmounts or updates.
<canvas ref={chartRef} />
renders a canvas element with a ref
to attach the Chart.js instance.For our pie chart, we want to view the percentage each budget category is taking, e.g., 20% for savings, 40% for food, etc.
We will head over to the PieChart.tsx
component and perform the following updates.
BarChart
component, we'll start by importing the necessary hooks and dependencies:1import React, { useEffect, useRef } from 'react';
2import Chart from 'chart.js/auto';
3import ChartDataLabels from 'chartjs-plugin-datalabels';
chartjs-plugin-datalabels
plugin:1Chart.register(ChartDataLabels);
categories
and amounts
.1interface PieChartProps {
2 categories: string[];
3 amounts: number[];
4}
PieChart
component. It will take categories
and amounts
as props. Then, we'll define the chartRef: Ref
function to reference the canvas element.1const PieChart: React.FC<PieChartProps> = ({ categories, amounts }) => {
2 const chartRef = useRef<HTMLCanvasElement | null>(null);
useEffect
hook like this:1useEffect(() => {
2 if (chartRef.current) {
3 const totalAmount = amounts.reduce((acc, amount) => acc + amount, 0);
4
5 const chartInstance = new Chart(chartRef.current, {
6 type: "pie",
7 data: {
8 labels: categories,
9 datasets: [
10 {
11 label: "Budget Amount",
12 data: amounts,
13 backgroundColor: [
14 "rgba(255, 99, 132, 0.6)",
15 "rgba(54, 162, 235, 0.6)",
16 "rgba(255, 206, 86, 0.6)",
17 "rgba(75, 192, 192, 0.6)",
18 "rgba(153, 102, 255, 0.6)",
19 "rgba(255, 159, 64, 0.6)",
20 ],
21 borderColor: [
22 "rgba(255, 99, 132, 1)",
23 "rgba(54, 162, 235, 1)",
24 "rgba(255, 206, 86, 1)",
25 "rgba(75, 192, 192, 1)",
26 "rgba(153, 102, 255, 1)",
27 "rgba(255, 159, 64, 1)",
28 ],
29 borderWidth: 1,
30 },
31 ],
32 },
33 options: {
34 responsive: true,
35 maintainAspectRatio: false,
36 plugins: {
37 title: {
38 display: true,
39 text: "Budget data - pie chart",
40 padding: {
41 top: 10,
42 bottom: 30,
43 },
44 },
45 datalabels: {
46 color: "white",
47 formatter: (value, context) => {
48 const percentage = ((value / totalAmount) * 100).toFixed(2);
49 return `${percentage}%`;
50 },
51 },
52 },
53 },
54 });
55
56 return () => {
57 chartInstance.destroy();
58 };
59 }
60}, [categories, amounts]);
We initialized the useEffect
hook and added a totalAmount
function to calculate the percentage of each category compared to other categories.
Just like the bar chart, the type: 'pie'
specifies that the chart is a pie chart.
The backgroundColor
and borderColor
arrays specify the colors for the pie chart segments.
The options: { responsive: true }
object makes the pie chart responsive. We added the maintainAspectRatio: false
in the options object to ensure that the chart does not maintain the default aspect ratio and can scale to the size of its container.
NOTE: The default size can be quite large and take up a lot of space on your page, so you can set width and height to reduce pie chart size.
The title
property inside the plugins
object sets the title display to true,
which displays the chart's title.
The datalabels
object contains each category's percentage values and sets the percentage text's color to white.
We returned the canvas element by wrapping the canvas element in a div
element with specific width and height styles.
We gave the canvas element width and height attributes to set its width and height.
1return (
2 <div style={{ marginTop: "15px", width: "700px", height: "700px" }}>
3 <canvas
4 ref={chartRef}
5 width="700"
6 height="700"
7 aria-label="Hello ARIA World"
8 role="img"
9 />
10 </div>
11);
You can adjust your chart size any way you want it to look.
We've modified the overview page by preparing it for the charts and writing the code. Now, we need to display the charts on the overview page.
In the JSX of our Overview.tsx
component, we'll render the UI:
1<main>
2 <div>
3 <section>
4 <h2>OVERVIEW</h2>
5 <div>
6 <button
7 onClick={() => setChartType("bar")}
8 className={`mx-2 py-2 px-3 ${chartType === "bar" ? "bg-teal-500 text-white" : "bg-gray-200 text-gray-700"} rounded-lg`}
9 >
10 Bar Chart
11 </button>
12 <button
13 onClick={() => setChartType("pie")}
14 className={`mx-2 py-2 px-3 ${chartType === "pie" ? "bg-teal-500 text-white" : "bg-gray-200 text-gray-700"} rounded-lg`}
15 >
16 Pie Chart
17 </button>
18 </div>
19 </section>
20 <section className="mt-8">
21 {chartType === "bar" ? (
22 <BarChart categories={categories} amounts={amounts} />
23 ) : (
24 <PieChart categories={categories} amounts={amounts} />
25 )}
26 </section>
27 </div>
28</main>;
We created a div
element with two buttons. Each button has an onClick
handler that sets the chart type to either 'bar' or 'pie.'
The buttons are styled based on the current chartType
state, representing the type we want to display.
The page then conditionally renders either the BarChart
or PieChart
component based on the current value of chartType.
If chartType
is 'bar'
, the BarChart
component is rendered. If chartType
is 'pie'
, the PieChart
component is rendered.
Both components receive categories
and amounts
as props to display the data.
Now our charts are ready.
Here's what our bar chart should look like:
Here's what our pie chart should look like:
If we edit a budget item from the budget page, like the category it falls under or the amount, the charts will adjust to match the new budget value.
Items and values will be dynamically added to the charts if we add more budgets via the budget page. Change the background and border colors and play around with the values.
Let's add a little animation to our charts.
First, in the bar chart, we'll add the easeOutElastic
animation property.
options
object called animation
. This property will be an object with two values - duration
and easing
.1animation: {
2 duration: 6000, // Increase or decrease the duration
3 easing: 'easeOutElastic', // Using the easing function
4},
We set the duration to 6000 milliseconds (6 seconds) to make the animation slower and more noticeable. When our visualization loads, the bars elasticate for 6 seconds before setting to their proper position.
In the pie chart, we'll add a bouncing animation using the easeOutBounce
property.
animation
property inside the options
object with two values to do this.1animation: {
2 duration: 6000,
3 easing: 'easeOutBounce',
4},
When we load our page, we'll see the bar chart's elastic animation and the pie segments' bouncing animation.
Check out the chart.js documentation site for a full list of other animations and transitions you can add to the charts.
We can use other charts to visualize financial data, but we will only use bar and pie charts for this article. If you're interested in exploring more visualizations with Chart.js, check out other chart types.
You can apply this concept to visualize other parts of your application. For example, you can visualize expenses for a few months to see how much you spent in a particular month compared to other months.
This is useful for making financial decisions and identifying trends.
In Part 2 of this tutorial series, we reviewed an overview of charts, set up Pie and Bar charts in Next.js using Chart.js and Strapi CMS, prepared them for data, added animation, and rendered them.
This project's GitHub repo has been updated to include financial data visualization.
Watch for the last part (part 3) of this blog series, where we'll add authentication and a personalized reporting feature to our finance tracker application.
Juliet is a developer and technical writer whose work aims to break down complex technical concepts and empower developers of all levels.