DEV Community

Cover image for Build a Finance Tracker with Next.js, Strapi, and Chartjs: Part 2
Juliet Ofoegbu for Strapi

Posted on

Build a Finance Tracker with Next.js, Strapi, and Chartjs: Part 2

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.

Tutorial Outline

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 our financial data using bar charts and pie charts.

Introduction to Data Visualization

Why is it important to visualize financial data?
Visualizing financial data is essential for several reasons such as:

  1. It provides clarity. When complex, we visually represent using charts and graphs, which makes it easier to understand than plain text.
  2. It helps track financial goals and monitors progress over time. They provide a clear view of budgets, income, expenses, and overall financial health.
  3. Visualization helps identify trends in income and spending, which can help you make informed decisions about your finances.

Overview of Chart.js

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.

Install Chart.js in the Next.js Project

To visualize our application's budget data, we'll use two charts (a bar chart and a pie chart).

  • To get started, let's install the Chart.js library using this command:
npm install chart.js
Enter fullscreen mode Exit fullscreen mode
  • We'll also install the 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
Enter fullscreen mode Exit fullscreen mode
  • Since we'll be using two charts, let's create two components for the charts inside our budget folder, located inside the dashboard folder. We'll name them - BarChart.tsx and PieChart.tsx.

Prepare the Overview.tsx Component for Receiving Chart Data

If 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.

"use client";
import React, { useState, useEffect } from "react";
import axios from "axios";
import BarChart from "./budget/BarChart";
import PieChart from "./budget/PieChart";
Enter fullscreen mode Exit fullscreen mode
  • We'll create states to store the budget and chart data types. The states will enable users to select which chart visual they want to view (bar chart or pie chart), the default being bar chart:
const [budgets, setBudgets] = useState<{ category: string; amount: number }[]>(
  [],
);
const [chartType, setChartType] = useState<"bar" | "pie">("bar");
Enter fullscreen mode Exit fullscreen mode
  • We'll implement the 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.
useEffect(() => {
     ...
}, []);
Enter fullscreen mode Exit fullscreen mode
  • Inside the 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.
  • The response 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.
  • The setBudgets(data) will then update the budget state with the fetched and mapped data.
const fetchBudgets = async () => {
  try {
    const res = await axios.get(
      "http://localhost:1337/api/budgets?populate=budget",
    );
    const data = res.data.data.map((budget: any) => ({
      category: budget.attributes.category,
      amount: budget.attributes.amount,
    }));
    setBudgets(data);
  } catch (error) {
    console.error("Error fetching budgets:", error);
  }
};

fetchBudgets();
Enter fullscreen mode Exit fullscreen mode
  • We'll call the fetchBudgets function after defining it within the useEffect hook.

  • After that, we will store the budget category and amount inside variables categories and amounts:

const categories = budgets.map((budget) => budget.category);
const amounts = budgets.map((budget) => budget.amount);
Enter fullscreen mode Exit fullscreen mode

Visualize Budget Categories With Bar Chart

It's time to write the functionalities to create these charts.

We will navigate to the BarChart.tsx component.

  • We'll first import the necessary hooks and the chart.js library. We'll use the auto package to ensure all features from chart.js are available.
import React, { useEffect, useRef } from "react";
import Chart from "chart.js/auto";
Enter fullscreen mode Exit fullscreen mode
  • We'll define an interface for the props expected by the component - arrays for categories and amounts.
interface BarChartProps {
  categories: string[];
  amounts: number[];
}
Enter fullscreen mode Exit fullscreen mode
  • The 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.
const BarChart: React.FC<BarChartProps> = ({ categories, amounts }) => {
const chartRef = useRef<HTMLCanvasElement | null>(null);
Enter fullscreen mode Exit fullscreen mode
  • Using the useEffect hook, we'll create the chart when the component mounts or when the categories and amounts props change:
useEffect(() => {
  if (chartRef.current) {
    const chartInstance = new Chart(chartRef.current, {
      type: "bar",
      data: {
        labels: categories,
        datasets: [
          {
            label: "Budget Amount",
            data: amounts,
            backgroundColor: [
              "rgba(75, 192, 192, 0.2)",
              "rgba(54, 162, 235, 0.2)",
              "rgba(153, 102, 255, 0.2)",
            ],
            borderColor: [
              "rgb(75, 192, 192)",
              "rgb(54, 162, 235)",
              "rgb(153, 102, 255)",
            ],
            borderWidth: 1,
          },
        ],
      },
      options: {
        scales: {
          y: {
            beginAtZero: true,
          },
        },
        plugins: {
          title: {
            display: true,
            text: "Budget data - bar chart",
            padding: {
              top: 10,
              bottom: 30,
            },
          },
        },
      },
    });

    return () => {
      chartInstance.destroy();
    };
  }
}, [categories, amounts]);

return <canvas ref={chartRef} />;
Enter fullscreen mode Exit fullscreen mode
  • 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.

  • Lastly, the <canvas ref={chartRef} /> renders a canvas element with a ref to attach the Chart.js instance.

Visualize Budget Categories With Pie Chart

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.

  • Similar to the BarChart component, we'll start by importing the necessary hooks and dependencies:
    javascript
    import React, { useEffect, useRef } from 'react';
    import Chart from 'chart.js/auto';
    import ChartDataLabels from 'chartjs-plugin-datalabels';

  • We'll include this method from the chartjs-plugin-datalabels plugin:
    javascript
    Chart.register(ChartDataLabels);

  • We'll then define the interface for the props expected by the component - arrays for categories and amounts.
    javascript
    interface PieChartProps {
    categories: string[];
    amounts: number[];
    }

  • Next, we'll define the PieChart component. It will take categories and amounts as props. Then, we'll define the chartRef: Ref function to reference the canvas element.
    javascript
    const PieChart: React.FC<PieChartProps> = ({ categories, amounts }) => {
    const chartRef = useRef<HTMLCanvasElement | null>(null);

  • Next, we initialize the chart inside a useEffect hook like this:
    `javascript
    useEffect(() => {
    if (chartRef.current) {
    const totalAmount = amounts.reduce((acc, amount) => acc + amount, 0);

    const chartInstance = new Chart(chartRef.current, {
    type: "pie",
    data: {
    labels: categories,
    datasets: [
    {
    label: "Budget Amount",
    data: amounts,
    backgroundColor: [
    "rgba(255, 99, 132, 0.6)",
    "rgba(54, 162, 235, 0.6)",
    "rgba(255, 206, 86, 0.6)",
    "rgba(75, 192, 192, 0.6)",
    "rgba(153, 102, 255, 0.6)",
    "rgba(255, 159, 64, 0.6)",
    ],
    borderColor: [
    "rgba(255, 99, 132, 1)",
    "rgba(54, 162, 235, 1)",
    "rgba(255, 206, 86, 1)",
    "rgba(75, 192, 192, 1)",
    "rgba(153, 102, 255, 1)",
    "rgba(255, 159, 64, 1)",
    ],
    borderWidth: 1,
    },
    ],
    },
    options: {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
    title: {
    display: true,
    text: "Budget data - pie chart",
    padding: {
    top: 10,
    bottom: 30,
    },
    },
    datalabels: {
    color: "white",
    formatter: (value, context) => {
    const percentage = ((value / totalAmount) * 100).toFixed(2);
    return ${percentage}%;
    },
    },
    },
    },
    });

    return () => {
    chartInstance.destroy();
    };
    }
    }, [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.

javascript
return (
<div style={{ marginTop: "15px", width: "700px", height: "700px" }}>
<canvas
ref={chartRef}
width="700"
height="700"
aria-label="Hello ARIA World"
role="img"
/>
</div>
);

You can adjust your chart size any way you want it to look.

Render the Pie and Bar Charts in the Overview Page

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:

html
<main>
<div>
<section>
<h2>OVERVIEW</h2>
<div>
<button
onClick={() => setChartType("bar")}
className={
mx-2 py-2 px-3 ${chartType === "bar" ? "bg-teal-500 text-white" : "bg-gray-200 text-gray-700"} rounded-lg}
>
Bar Chart
</button>
<button
onClick={() => setChartType("pie")}
className={
mx-2 py-2 px-3 ${chartType === "pie" ? "bg-teal-500 text-white" : "bg-gray-200 text-gray-700"} rounded-lg}
>
Pie Chart
</button>
</div>
</section>
<section className="mt-8">
{chartType === "bar" ? (
<BarChart categories={categories} amounts={amounts} />
) : (
<PieChart categories={categories} amounts={amounts} />
)}
</section>
</div>
</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:

bar chart.png

Here's what our pie chart should look like:

pie chart.png

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.

Add Animation to the Charts

Let's add a little animation to our charts.

First, in the bar chart, we'll add the easeOutElastic animation property.

  • To do this, we'll include another property in the options object called animation. This property will be an object with two values - duration and easing. javascript animation: { duration: 6000, // Increase or decrease the duration easing: 'easeOutElastic', // Using the easing function },

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.

  • We'll include the animation property inside the options object with two values to do this. javascript animation: { duration: 6000, easing: 'easeOutBounce', },

Animation Demo

When we load our page, we'll see the bar chart's elastic animation and the pie segments' bouncing animation.

add pie and barcharts in nextjs with chartjs and strapi demo.gif

More Animations?

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.

Conclusion

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.

Top comments (0)