DEV Community

Cover image for Chart.js in Next.js 15: Create Dynamic Data Visualizations
Willochs Ojigbo
Willochs Ojigbo

Posted on

Chart.js in Next.js 15: Create Dynamic Data Visualizations

This tutorial focuses on integrating Chart.js with Next.js, a powerful React framework. Readers will learn how to create dynamic, interactive charts that can be easily integrated into their Next.js applications. The tutorial will cover installation, configuration, and practical examples to illustrate how to leverage Chart.js for data visualization. By the end of the tutorial, readers will be equipped with the skills to implement various types of charts, customize them, and understand best practices for performance optimization.

In this tutorial, we will cover the following main topics:

  1. Introduction to Chart.js and Next.js
  2. Creating Your First Chart
  3. Customizing and Optimizing Charts in Next.js

In this tutorial, we will create a chart that visualizes diagnosis history, as illustrated in the following figure:

Chartjs
Fig 1: Chart Showing Diagnosis History

Introduction to Chart.js in Next.js

Data visualization is an important part of modern web applications. Whether you're displaying sales data, user analytics, or financial reports, showing numbers in a well-structured chart helps users understand data quickly.

In this section, we will introduce Chart.js, a JavaScript library that makes it easy to create interactive and beautiful charts. You will learn why Chart.js is a great choice for data visualization and how to integrate it into a Next.js application.

By the end of this section, you’ll have a solid understanding of:

  • What Chart.js is and why it is useful.
  • How to install and set up Chart.js in a Next.js project.
  • How Next.js’s rendering methods impact Chart.js and how to use it properly in a React-based framework.

What is Chart.js?

Chart.js is a popular JavaScript library used for creating beautiful and interactive charts. It supports various chart types, such as line, bar, pie, and more. Developers appreciate Chart.js for its simplicity and flexibility, making it easy to visualize data in web applications. The library is lightweight and can be easily integrated into different frameworks, including React.

Why Use Chart.js in Next.js Applications?

Next.js is a powerful React framework that enables server-side rendering, static site generation, and easy routing. Integrating Chart.js into a Next.js application allows you to leverage these features while creating dynamic, data-driven visualizations. By using Chart.js in Next.js, you can:

  • Improve Performance: Server-side rendering can speed up initial load times, making charts appear faster for users.
  • Enhance User Experience: Interactive charts provide a better way to present information, allowing users to gain insights quickly.
  • Simplify Data Fetching: Next.js makes it easy to fetch data from APIs, which can then be visualized using Chart.js.

Installing Chart.js and Dependencies

To get started with Chart.js in a Next.js project, you first need to set up your Next.js environment. Here’s how to do it:

Step 1: Open your terminal and run the following command:

npx create-next-app@latest my-chart-app
Enter fullscreen mode Exit fullscreen mode

Replace my-chart-app with your preferred project name. This command will create a new directory with all the necessary files to start a Next.js application.

or you clone the repo:

git clone https://github.com/Willochs316/chart-app.git
Enter fullscreen mode Exit fullscreen mode

Step 2: Navigate to Your Project Directory:

cd my-chart-app
Enter fullscreen mode Exit fullscreen mode

Step 3: Install Chart.js and React-Chartjs-2:
React-Chartjs-2 is a wrapper for Chart.js that makes it easier to use within React applications. Install both libraries using:

npm install chart.js react-chartjs-2
Enter fullscreen mode Exit fullscreen mode

Once installed, you can check your package.json file to confirm that both dependencies have been added:

  "dependencies": {
    "chart.js": "^4.4.8",
    "next": "15.2.3",
    "react": "^19.0.0",
    "react-chartjs-2": "^5.3.0",
    "react-dom": "^19.0.0"
  },
Enter fullscreen mode Exit fullscreen mode

Understanding Next.js’s Rendering Methods and How They Affect Chart.js

Next.js provides different ways to render components:

  • Server-Side Rendering (SSR) – Renders pages on the server before sending them to the browser.
  • Static Site Generation (SSG) – Pre-builds pages at build time.
  • Client-Side Rendering (CSR) – Renders components on the client side after the page loads.

Since Chart.js runs in the browser (it requires window and document objects), we cannot use it directly in server-rendered pages. If we try to import Chart.js in a page that is server-rendered, it may cause errors like:

ReferenceError: window is not defined
Enter fullscreen mode Exit fullscreen mode

Solution

"use client"

Summary of What You Have Learned

In this section, you learned:

  • What Chart.js is and why it’s a great tool for data visualization.
  • Why Next.js is a good choice for integrating Chart.js.
  • How to install Chart.js and react-chartjs-2 in a Next.js project.
  • How Next.js’s rendering methods affect Chart.js and how to prevent errors using dynamic imports.

What’s Next?

Now that we have installed Chart.js and understand its role in a Next.js application, it’s time to create our first charts!

Creating Your First Chart with Chart.js and JSON Server

Now that we have Chart.js set up in our Next.js project, it’s time to create our first chart! But instead of using static data, we’ll take a real-world approach and fetch our data from an API.

To do this, we’ll use JSON Server, a simple tool that allows us to create a REST API using a local db.json file. This will help us understand how to fetch and display real-time data inside a Chart.js chart in a Next.js application.

By the end of this section, you’ll learn:

  • How to structure and configure data for Chart.js.
  • How to use the react-chartjs-2 wrapper to integrate Chart.js with React components in Next.js.
  • How to fetch data from a REST API (JSON Server) and use it in a chart.

Setting Up JSON Server

First, we need to set up JSON Server to serve our mock data. Follow these steps:

Step 1: Install JSON Server

Open your terminal and install JSON Server globally:

npm install -g json-server
Enter fullscreen mode Exit fullscreen mode

You can check if the installation was successful by running:

json-server --version
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Folder and a db.json File
Inside your Next.js project, create a folder named _data, then inside that folder, create a file called db.json.

my-chart-app/
-- _data/
   ├── db.json
-- pages/
-- components/
-- package.json
-- ...
Enter fullscreen mode Exit fullscreen mode

JSON Server

Fig 2: _data Folder and db.json file

Now, open db.json and add the following sample medical diagnosis history data:

{
  "diagnosis": [
    {
      "month": "March",
      "year": 2024,
      "blood_pressure": {
        "systolic": {
          "value": 160,
          "levels": "Higher than Average"
        },
        "diastolic": {
          "value": 78,
          "levels": "Lower than Average"
        }
      },
      "heart_rate": {
        "value": 78,
        "levels": "Lower than Average"
      },
      "respiratory_rate": {
        "value": 20,
        "levels": "Normal"
      },
      "temperature": {
        "value": 98.6,
        "levels": "Normal"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

app/types/diagnosis.ts

export type DiagnosisEntry = {
    month: string;
    year: number;
    blood_pressure: {
      systolic: {
        value: number;
        levels: string;
      };
      diastolic: {
        value: number;
        levels: string;
      };
    };
    heart_rate: {
      value: number;
      levels: string;
    };
    respiratory_rate: {
      value: number;
      levels: string;
    };
    temperature: {
      value: number;
      levels: string;
    };
  };
Enter fullscreen mode Exit fullscreen mode

Step 3: Start JSON Server
Now click the plus icon on your code editor (vscode) to add a new terminal to your existing terminal, start the JSON Server by running:

json-server --watch _data/db.json --port 4000
Enter fullscreen mode Exit fullscreen mode

This will create a local API Endpoints at:

http://localhost:4000/diagnosis
Enter fullscreen mode Exit fullscreen mode

JSON Server

Fig 3: JSON Server Running
You can test it by opening your browser and visiting http://localhost:4000/diagnosis. You should see the JSON data displayed.

Image description

Fetching Data in Next.js

Now that we have our API running, let’s fetch the data in Next.js.

Step 1: Create a Function to Fetch Data
Inside your app folder in your Next.js project, create a file called api.ts inside a folder named utils/. This file will contain a function to fetch data from the JSON server.

app/utils/api.ts

export async function getDiagnosis() {
    try {
      const res = await fetch("http://localhost:4000/diagnosis");
      if (!res.ok) throw new Error("Failed to fetch data");

      const data = await res.json();

      // Get today's date
      const currentDate = new Date();
      const currentMonth = currentDate.getMonth() + 1; // Months are 0-based in JS
      const currentYear = currentDate.getFullYear();

      // Convert month names to numbers for easy filtering
      const monthMap: { [key: string]: number } = {
        January: 1, February: 2, March: 3, April: 4, May: 5, June: 6,
        July: 7, August: 8, September: 9, October: 10, November: 11, December: 12
      };

      // Get the last 6 months of data
      const last6Months = data.filter((entry: any) => {
        const entryMonth = monthMap[entry.month];
        const entryYear = entry.year;

        const monthDiff = (currentYear - entryYear) * 12 + (currentMonth - entryMonth);
        return monthDiff >= 0 && monthDiff < 6; // Only last 6 months
      });

      return last6Months;
    } catch (error) {
      console.error("Error fetching diagnosis history:", error);
      return [];
    }
  }
Enter fullscreen mode Exit fullscreen mode

Creating a Chart Component in Next.js

Now, let’s create a Chart.js component to display the blood pressure levels from our API.

Step 1: Create a Chart Component
Inside your app folder in your Next.js project, create a folder named components/, and inside it, create a file called chart.tsx.

app/components/chart.tsx

"use client";
import React from "react";
import dynamic from "next/dynamic";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend,
} from "chart.js";
import Image from "next/image";
import ArrowDown from "@/public/icons/arrow-down.svg";
import ArrowUp from "@/public/icons/arrow-up.svg";
import { DiagnosisEntry } from "@/app/types/diagnosis";
import ExpandMore from "@/public/icons/expand-more.svg";

// Register the components
ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend
);

// Dynamically import Line from react-chartjs-2
const LineChart = dynamic(
  () => import("react-chartjs-2").then((mod) => mod.Line),
  { ssr: false }
);

type MyChartProps = {
  diagnosis: DiagnosisEntry[];
};

export function MyChart({ diagnosis }: MyChartProps) {
  // Sort data by date to ensure correct order
  diagnosis.sort((a: DiagnosisEntry, b: DiagnosisEntry) => {
    const monthOrder: { [key: string]: number } = {
      January: 1,
      February: 2,
      March: 3,
      April: 4,
      May: 5,
      June: 6,
      July: 7,
      August: 8,
      September: 9,
      October: 10,
      November: 11,
      December: 12,
    };

    return a.year - b.year || monthOrder[a.month] - monthOrder[b.month];
  });

  // Extract labels and data
  const labels = diagnosis.map(
    (entry: DiagnosisEntry) => `${entry.month.slice(0, 3)}, ${entry.year}`
  );
  const systolicData = diagnosis.map(
    (entry: DiagnosisEntry) => entry.blood_pressure.systolic.value
  );
  const diastolicData = diagnosis.map(
    (entry: DiagnosisEntry) => entry.blood_pressure.diastolic.value
  );

  return (
    <>
      <div className="flex items-start mt-[40px] bg-[#fff] p-[19px] rounded-[12px] w-1/2 h-auto mx-auto">
        <div className="w-[418px]">
          <div className="flex items-center justify-between w-full">
            <h2 className="text-[#072635] text-[18px] font-bold">
              Diagnosis History
            </h2>

            <button className="flex items-center justify-between w-[119px]">
              <span className="text-[14px] text-[#072635] font-light">
                Last 6 months
              </span>

              <Image src={ExpandMore} alt="" />
            </button>
          </div>

          <div className="mt-[20px] w-full">
            <LineChart
              data={{
                labels: labels,
                datasets: [
                  {
                    label: "Systolic",
                    data: systolicData,
                    backgroundColor: "#5FC3D6",
                    borderColor: "#5FC3D6",
                    borderWidth: 1,
                  },
                  {
                    label: "Diastolic",
                    data: diastolicData,
                    backgroundColor: "#EC5252",
                    borderColor: "#EC5252",
                    borderWidth: 1,
                  },
                ],
              }}
              options={{
                responsive: true,
                maintainAspectRatio: true,
                scales: {
                  x: {
                    beginAtZero: true,
                  },
                  y: {
                    beginAtZero: false,
                    min: 60,
                  },
                },
              }}
            />
          </div>
        </div>

        <div className="w-[208px] flex flex-col items-start justify-center ml-[39.05px]">
          <div className="flex flex-col items-start justify-center w-full">
            <div className="flex items-center w-full">
              <span className="w-[14px] h-[14px] rounded-full bg-[#5FC3D6] border border-[#FFFFFF]"></span>
              <span className="text-[#072635] ml-[4px] text-[14px] font-normal">
                Systolic
              </span>
            </div>

            <h2 className="text-[#072635] text-[22px] font-bold mt-[8px]">
              {diagnosis[5].blood_pressure.systolic.value}
            </h2>

            <div className="flex items-center w-full mt-[8px]">
              <Image
                className="w-[10px] h-[5px]"
                width={10}
                height={5}
                src={ArrowUp}
                alt=""
              />
              <span className="text-[#072635] text-[14px] ml-[8px] font-light">
                Higher than Average
              </span>
            </div>

            <hr className="w-full h-[1px] mt-[16px] bg-[#CBC8D4]" />

            <div className="flex items-center w-full mt-[19px]">
              <span className="w-[14px] h-[14px] rounded-full bg-[#EC5252] border border-[#FFFFFF]"></span>
              <span className="text-[#072635] ml-[4px] text-[14px] font-normal">
                Diastolic
              </span>
            </div>
            <h2 className="text-[#072635] text-[22px] font-bold mt-[8px]">
              {diagnosis[5].blood_pressure.diastolic.value}
            </h2>

            <div className="flex items-center w-full mt-[8px]">
              <Image
                className="w-[10px] h-[5px]"
                width={10}
                height={5}
                src={ArrowDown}
                alt=""
              />
              <span className="text-[#072635] text-[14px] ml-[8px] font-light">
                Lower than Average
              </span>
            </div>
          </div>
        </div>
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

page.tsx

import { getDiagnosis } from "@/app/utils/app";
import { MyChart } from "@/app/components/chart";

export default async function Home() {
  const diagnosis = await getDiagnosis();

  return (
    <div className="max-w-full w-full">
      <MyChart diagnosis={diagnosis} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Follow me

Image description Image description Image description Image description

Image of Timescale

📊 Benchmarking Databases for Real-Time Analytics Applications

Benchmarking Timescale, Clickhouse, Postgres, MySQL, MongoDB, and DuckDB for real-time analytics. Introducing RTABench 🚀

Read full post →

Top comments (0)

Image of Timescale

📊 Benchmarking Databases for Real-Time Analytics Applications

Benchmarking Timescale, Clickhouse, Postgres, MySQL, MongoDB, and DuckDB for real-time analytics. Introducing RTABench 🚀

Read full post →

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️