Dashboard Stats Overview

Dashboard Stats Overview: Visualizing Task Progress

Welcome back! In the previous lesson, you built the foundation of your application by creating an app shell and a dashboard layout with consistent navigation and styling. Now, we’re going to bring that layout to life by adding a dashboard overview that displays important task statistics.

A dashboard is a central place where users can quickly see the most important information at a glance. In our case, that means showing how many tasks exist, how many are completed, and how many are incomplete. This gives users a clear sense of progress and helps them stay organized.

By the end of this lesson, you’ll know how to:

  • Fetch data from your backend API.
  • Understand what “fetching” means and what a GET request does.
  • Calculate statistics from raw task data.
  • Display those statistics in styled components using Tailwind CSS.

Recap: App Shell and Data Source

In the last lesson, you created:

  • A root layout (layout.tsx) that provides global HTML structure.
  • A dashboard layout ((dashboard)/layout.tsx) that provides navigation and a header.
  • A page.tsx that wires everything together.

Here’s that main page.tsx file again for reference:

// src/app/page.tsx
import DashboardLayout from './(dashboard)/layout';
import DashboardPage from './(dashboard)/page';

export default function Page() {
  return (
    <DashboardLayout>
      <DashboardPage />
    </DashboardLayout>
  );
}

In this unit, we focus on DashboardPage. This is where we’ll connect to the backend, fetch task data, and show meaningful statistics.

Understanding the Backend Route

Before we fetch data, let’s remind ourselves where that data comes from.

In your project, the backend lives under src/app/api. Specifically, the file src/app/api/tasks/route.ts defines the tasks API route. This is a GET endpoint, which means when your browser (or frontend code) requests data from /api/tasks, the backend responds with a JSON object containing tasks.

For example, a GET request to /api/tasks might return something like this:

{
  "data": [
    { "id": 1, "title": "Write lesson", "content": "Finish writing explanations", "completed": true },
    { "id": 2, "title": "Review code", "content": "Check starter files", "completed": false }
  ],
  "meta": { "timestamp": "2025-10-04T12:34:56Z" }
}

Breaking this down:

  • "data" is an array of tasks. Each task has fields like id, title, content, and completed.
  • "meta" contains extra information, such as when the response was created.

This response is in JSON (JavaScript Object Notation), which is a text-based format commonly used for sending data between servers and clients. JSON looks like JavaScript objects and is easy for both humans and code to read.

What Does “Fetching” Data Mean?

When we say “fetching data,” we mean sending a request from the frontend to the backend and then waiting for a response.

  1. The frontend sends an HTTP request.
  2. In this case, it’s a GET request, which is the standard way to ask a server for data (as opposed to POST, which creates data, or DELETE, which removes it).
  3. The backend receives the request at /api/tasks, looks up the tasks, and sends back a JSON response.
  4. The frontend receives that JSON and uses it to update the UI.

So when our code calls fetch('/api/tasks'), it’s like asking:

“Hey backend, please give me the current list of tasks.”

And the backend replies with a JSON list of tasks.

The API Client: Talking to the Backend

To make this process easier and consistent, we use a small helper called an API client. Here’s the code:

// src/lib/apiClient.ts
// Minimal API client (GET only); we’ll upgrade this in a later unit.
async function get<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Failed to fetch');
  const json = await res.json();
  return 'data' in json ? json.data : json;
}

export const api = { get };

Step by step:

  • The get function calls fetch(url).
  • fetch is a built-in browser function for making HTTP requests.
  • Here, url will be something like /api/tasks.
  • If the response isn’t OK (for example, the server returned an error), we throw an error.
  • Otherwise, we parse the JSON response with await res.json().
  • Many of our backend routes wrap results in a data field, so we return the data property when that key is present; otherwise, we return the whole JSON response.

This keeps all our data-fetching logic in one place. Instead of writing fetch logic every time, we just call api.get.

Fetching Data With SWR

Next, let’s see how our dashboard uses this API client together with SWR (a data-fetching library for React):

// src/app/(dashboard)/page.tsx
'use client';
import useSWR from 'swr';
import { api } from '@/lib/apiClient';

export default function DashboardPage() {
  const { data } = useSWR('/api/tasks', api.get<any[]>);
  const tasks = Array.isArray(data) ? data : [];
  // ...
}

Here’s what’s happening:

  • useSWR is a React hook for data fetching.
  • It takes two arguments:
    1. The key (or endpoint) → '/api/tasks'.
    2. A fetcher function → api.get, which actually does the request.

SWR automatically:

  • Calls our API and loads the data.
  • Keeps it fresh if the page refocuses or reconnects.
  • Provides states like loading and error if we want to handle them.

By the time the request succeeds, data contains the array of tasks returned from the backend. We then check Array.isArray(data) to ensure we always have a safe array to work with.

Calculating Task Statistics

Now that we have the tasks, we want to calculate statistics that are meaningful to users:

const total = tasks.length;
const completed = tasks.filter((t) => t.completed).length;
const incomplete = total - completed;
  • tasks.length → the total number of tasks.
  • .filter((t) => t.completed) → a new array containing only completed tasks. We take its length.
  • incomplete → the remainder (total minus completed).

This is a good example of using array methods in JavaScript to transform and summarize data.

Displaying Stats With a Card Component

The last step is to display the statistics on the screen. For that, we create a simple helper component called StatCard:

function StatCard({ title, value }: { title: string; value: number }) {
  return (
    <div className="rounded-lg border bg-white p-4 shadow-sm">
      <div className="text-sm text-gray-500">{title}</div>
      <div className="mt-1 text-3xl font-bold">{value}</div>
    </div>
  );
}

Explanation:

  • The card displays a title (like “Completed”) and a value (like 5).
  • Tailwind classes style it:
    • rounded-lg = rounded corners.
    • border bg-white = card appearance.
    • shadow-sm = subtle drop shadow.

Finally, we render three cards in a responsive grid:

return (
  <div className="space-y-6">
    <h1 className="text-2xl font-semibold">Dashboard</h1>
    <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
      <StatCard title="Total Tasks" value={total} />
      <StatCard title="Completed" value={completed} />
      <StatCard title="Incomplete" value={incomplete} />
    </div>
  </div>
);

On mobile, the cards stack vertically (grid-cols-1). On larger screens, they show side by side in three columns (sm:grid-cols-3). This makes the layout responsive with just a few Tailwind classes.

How It All Fits Together

Let’s zoom out and connect the dots:

  • Backend route (/api/tasks) returns a JSON list of tasks when you send a GET request.
  • API client (apiClient.ts) wraps fetch so you can easily request and parse responses.
  • SWR hook calls the API client, keeps the data fresh, and stores it in data.
  • DashboardPage calculates totals and uses StatCards to display them.
  • Layouts (layout.tsx and (dashboard)/layout.tsx) wrap everything in consistent structure and styling.

This is a full round trip: from backend data, through fetching and processing, to rendering on the frontend.

Review and What’s Next

In this lesson, you:

  • Learned what it means to make a GET request and fetch data from an API.
  • Explored the /api/tasks backend route and saw the kind of JSON response it returns.
  • Used an API client and the SWR hook to request and manage data on the frontend.
  • Calculated total, completed, and incomplete tasks from the raw array.
  • Displayed those statistics with styled cards in the dashboard layout.

Next, you’ll practice fetching and presenting data yourself. You’ll strengthen your understanding of how the frontend and backend work together — the frontend requests data, the backend responds with JSON, and React renders it into a meaningful UI.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal