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.jschartjs-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-datalabelsbudget 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.