In this tutorial, we’ll guide you through the process of building a simple financial dashboard app using Next.js, React, and Chart.js. We’ll cover everything from setting up dummy data to creating reusable components, building a dashboard page, and styling the application. By the end, you’ll have a fully functioning dashboard that displays financial data, such as income and expenses, along with a visually appealing chart.

Step 1: Set Up the Project
First, create a new Next.js project by running the following commands in your terminal:
npx create-next-app@latest financial-dashboard
And choose to install following packages:
Need to install the following packages:
create-next-app@15.1.7
Ok to proceed? (y) y
✔ Would you like to use TypeScript? … No
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like your code inside a `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes
✔ Would you like to customize the import alias (`@/*` by default)? … Yes
✔ What import alias would you like configured? … @/*
Next, navigate to the financial-dashboard directory:
cd financial-dashboard
We will need a few libraries for data visualization.
npm install react-chartjs-2 chart.js chartjs-adapter-date-fns
Step 2: Setup Dummy Data API Route
We need to create a simple API route in Next.js to simulate the transactions data. This API will return a list of transactions that we can use in our application.
Create src/app/api/transactions/route.js
import { NextResponse } from "next/server";
export const GET = () => {
const transactions = [
{ id: 1, description: "Salary", amount: 2000, date: "2025-02-20" },
{ id: 2, description: "Groceries", amount: -150, date: "2025-02-21" },
{
id: 3,
description: "Electricity Bill",
amount: -100,
date: "2025-02-22",
},
{ id: 4, description: "Freelance Work", amount: 500, date: "2025-02-22" },
{ id: 5, description: "Dining Out", amount: -60, date: "2025-02-22" },
];
return new NextResponse(JSON.stringify(transactions), { status: 200 });
};
In this file, we define a simple GET request that returns an array of transactions. Each transaction has an id, description, amount, and date.
Step 2: Create reusable components
Now, let’s create some reusable React components for our dashboard. These components will include:
- BalanceCard: Displays the user’s balance.
- TransactionList: Shows a list of all transactions.
- Chart: Displays a bar chart visualizing the income and expenses.
BalanceCard Component
The BalanceCard component will display the total balance of the user. We’ll pass the balance as a prop to the component.
Create the file src/app/components/BalanceCard.js:
import React from "react";
const BalanceCard = ({ balance }) => {
return (
<div className="balance-card">
<h2>Your Balance</h2>
<p>${balance.toFixed(2)}</p>
</div>
);
};
export default BalanceCard;
TransactionList Component
The TransactionList component will display the list of transactions with their descriptions, amounts, and dates.
Create the file src/app/components/TransactionList.js:
import React from "react";
const TransactionList = ({ transactions }) => {
return (
<div className="transaction-list">
<h3 className="text-xl font-bold">Transactions</h3>
<ul>
{transactions.map((transaction) => (
<li key={transaction.id}>
<p className="font-bold">{transaction.description}</p>
<p>${transaction.amount.toFixed(2)}</p>
<p className="text-gray-500">{transaction.date}</p>
</li>
))}
</ul>
</div>
);
};
export default TransactionList;
Chart Component
The Chart component will display a bar chart showing the income and expenses by date. We’ll use Chart.js and React Chart.js 2 to render the chart.
Create the file src/app/components/Chart.js:
import { Bar } from "react-chartjs-2"; // Import Bar chart instead of Line
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
TimeScale,
} from "chart.js";
import "chartjs-adapter-date-fns"; // Import the date adapter for handling date formats
// Register chart.js components
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
TimeScale
);
const Chart = ({ aggregatedData }) => {
// Data for the chart
const labels = aggregatedData.map((data) => data.date); // Extract dates from the aggregated data
const incomeData = aggregatedData.map((data) => data.income); // Extract income values
const expensesData = aggregatedData.map((data) => data.expenses); // Extract expenses values
const data = {
labels: labels, // Dates on the x-axis
datasets: [
{
label: "Income",
data: incomeData, // Income values for each date
backgroundColor: "green",
borderColor: "green",
borderWidth: 1,
},
{
label: "Expenses",
data: expensesData, // Expenses values for each date
backgroundColor: "red",
borderColor: "red",
borderWidth: 1,
},
],
};
// Chart options to center the bars and fix the background
const options = {
responsive: true,
plugins: {
tooltip: {
callbacks: {
label: function (context) {
const value = context.raw;
return `$${value.toFixed(2)}`; // Format as currency
},
},
},
},
scales: {
x: {
type: "time", // Time scale for x-axis to display dates
time: {
unit: "day", // Show one tick per day
tooltipFormat: "P", // Use 'P' for full date format (e.g., February 22, 2025)
unitStepSize: 1, // Step size for the time unit (1 day at a time)
},
ticks: {
font: {
size: 14,
},
autoSkip: true,
maxRotation: 0, // Prevent label rotation
},
},
y: {
beginAtZero: true,
ticks: {
font: {
size: 14,
},
},
},
},
elements: {
bar: {
borderWidth: 1,
borderRadius: 5,
},
},
layout: {
padding: {
left: 0,
right: 0,
},
},
barPercentage: 0.8,
categoryPercentage: 0.5,
};
return <Bar data={data} options={options} />;
};
export default Chart;
Step 3: Setup the Dashboard Page
Now, let’s create the Dashboard Page. This page will display the balance card, transaction list, and chart. We’ll also fetch the transactions data from the API and calculate the total balance and aggregated income/expenses by date.
Create the file src/app/dashboard/page.js:
"use client";
import { useEffect, useState } from "react";
import BalanceCard from "../components/BalanceCard";
import TransactionList from "../components/TransactionList";
import Chart from "../components/Chart";
import styles from "../styles/Home.module.css";
export default function Dashboard() {
const [transactions, setTransactions] = useState([]);
const [balance, setBalance] = useState(0);
const [aggregatedData, setAggregatedData] = useState([]);
useEffect(() => {
const fetchTransactions = async () => {
const response = await fetch("/api/transactions");
const data = await response.json();
setTransactions(data);
calculateBalance(data);
aggregateDataByDate(data);
};
fetchTransactions();
}, []);
// Calculate total balance
const calculateBalance = (transactions) => {
const total = transactions.reduce(
(sum, transaction) => sum + transaction.amount,
0
);
setBalance(total);
};
// Aggregate income and expenses by date
const aggregateDataByDate = (transactions) => {
const dateData = {};
transactions.forEach((transaction) => {
const date = transaction.date;
// Initialize a new object that has income and expenses
if (!dateData[date]) {
dateData[date] = { income: 0, expenses: 0 };
}
if (transaction.amount > 0) {
dateData[date].income += transaction.amount; // Add to income
} else {
dateData[date].expenses += Math.abs(transaction.amount); // Add to expenses (remove negative sign)
}
console.log(dateData);
});
// Object.keys() gives us an array of all the unique dates in dateData (the keys).
const aggregated = Object.keys(dateData).map((date) => ({
date,
income: dateData[date].income,
expenses: dateData[date].expenses,
}));
setAggregatedData(aggregated); // Set aggregated data for use in the chart
};
return (
<div className={styles.container}>
<h1 className="font-bold">Financial Dashboard</h1>
<BalanceCard balance={balance} />
<div className={styles.chart}>
<Chart aggregatedData={aggregatedData} />
</div>
<TransactionList transactions={transactions} />
</div>
);
}
The aggregateDataByDate function:
- Takes an array of transactions, where each transaction contains a date and an amount.
- It iterates over the transactions and aggregates the income and expenses for each date.
- It stores this aggregated data in the dateData object, where each date is a key and the value is an object with income and expenses.
- It then creates an array (aggregated) of objects, where each object contains the date, income, and expenses for that date.
- Finally, it updates the state (setAggregatedData) with the aggregated data, which can be used elsewhere (e.g., in a chart or displayed to the user).
Step 4: Styles the Dashboard Page
To give our dashboard a clean and user-friendly design, let’s add some styles using CSS modules.
Create the file src/app/styles/Home.module.css:
/* Local styles */
.container {
padding: 20px;
font-family: Arial, sans-serif;
}
.title {
font-size: 2rem;
margin-bottom: 20px;
}
.balanceCard {
background-color: #f5f5f5;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.balanceCard h2 {
margin: 0;
}
.balanceCard p {
font-size: 1.5rem;
font-weight: bold;
}
.transactionList {
list-style-type: none;
padding: 0;
}
.transactionListItem {
background-color: #f9f9f9;
padding: 10px;
margin: 5px 0;
border-radius: 8px;
}
.transactionList h3 {
margin-top: 0;
}
.chart {
margin: 20px 0;
max-width: 600px;
}
Conclusion
In this tutorial, we’ve built a Financial Dashboard application using Next.js. We:
- Set up a dummy data API route to fetch transactions.
- Created reusable components for displaying balance, transactions, and a chart.
- Built the dashboard page to display the balance, transactions, and chart.
- Styled the page to make it visually appealing.
You can now further customize the app, enhance the functionality, or even add real data to the API. This serves as a great starting point for any financial dashboard or budget tracking application.