Craftly
Back to blog
· 9 min read

How to Build an Admin Dashboard with Next.js in 2026

Next.js TypeScript Design

Why Dashboards Matter for SaaS

Every SaaS product eventually needs an admin dashboard. Whether it's monitoring revenue, tracking active users, or managing customer accounts, dashboards are where data becomes actionable. A well-built dashboard is often the difference between a product that feels professional and one that feels like a prototype.

Yet dashboards are notoriously time-consuming to build from scratch. You need charts that work, tables that sort and filter, a sidebar that collapses on mobile, stat cards that update in real time, and a layout that stays coherent at every screen size. Get any one of these wrong and the whole thing falls apart.

In 2026, Next.js 16 with Tailwind CSS v4 is the strongest foundation for building admin dashboards. Here's how to approach it.

Key Components of a Modern Dashboard

Stat Cards

Stat cards are the first thing users see — a row of KPI tiles showing the numbers that matter most: total revenue, active users, conversion rate, churn. A good stat card includes the current value, a comparison to the previous period, and a trend indicator (up/down arrow with color coding).

```tsx

interface StatCardProps {

label: string;

value: string;

change: number;

changeLabel: string;

}

export function StatCard({ label, value, change, changeLabel }: StatCardProps) {

const isPositive = change >= 0;

return (

<div className="rounded-xl border border-border bg-card p-6">

<p className="text-sm text-muted-foreground">{label}</p>

<p className="mt-2 text-3xl font-bold">{value}</p>

<p className={isPositive ? "text-green-500" : "text-red-500"}>

{isPositive ? "+" : ""}{change}% {changeLabel}

</p>

</div>

);

}

```

Keep stat cards in a 2x2 or 4-column grid on desktop, stacking to a single column on mobile.

Charts

Charts are where most dashboard projects get complicated. You need to choose a library, understand its API, and make it work with your data format.

Choosing a Charting Library: Recharts vs Chart.js vs Tremor

**Recharts** is the most popular choice for React dashboards. It's built on SVG, composable, and integrates naturally with React's component model. The API is declarative and easy to read.

```tsx

import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts";

export function RevenueChart({ data }: { data: { date: string; revenue: number }[] }) {

return (

<ResponsiveContainer width="100%" height={300}>

<LineChart data={data}>

<XAxis dataKey="date" />

<YAxis />

<Tooltip />

<Line type="monotone" dataKey="revenue" stroke="#6366f1" strokeWidth={2} />

</LineChart>

</ResponsiveContainer>

);

}

```

**Chart.js** (via react-chartjs-2) is battle-tested and has more chart types, but the API is more imperative and less idiomatic in React. Good for teams coming from a non-React background.

**Tremor** provides pre-built dashboard components including charts, stat cards, and tables. It's the fastest path to a working dashboard, but less flexible for custom designs.

For most SaaS dashboards, Recharts is the right choice. It's flexible, actively maintained, and the community is large.

Data Tables

Tables are the most common dashboard component and the easiest to get wrong. A production-grade data table needs:

- Sortable columns (click header to sort, click again to reverse)

- Pagination (server-side for large datasets, client-side for small ones)

- Search and filter

- Row selection for bulk actions

- Responsive behavior (horizontal scroll or card view on mobile)

Build your table as a generic component that accepts column definitions and row data. This makes it reusable across different sections of the dashboard.

Sidebar

The sidebar is the backbone of your dashboard navigation. In 2026, the standard pattern is:

- **Desktop:** Fixed sidebar, 240px wide, with icons and labels

- **Tablet:** Collapsible sidebar, icons-only mode

- **Mobile:** Off-canvas drawer, triggered by a hamburger menu

```tsx

"use client";

import { useState } from "react";

export function Sidebar() {

const [collapsed, setCollapsed] = useState(false);

return (

<aside className={collapsed ? "w-16" : "w-60"} style={{ transition: "width 200ms" }}>

<button onClick={() => setCollapsed(!collapsed)}>

{collapsed ? "Expand" : "Collapse"}

</button>

<nav>{/* nav items */}</nav>

</aside>

);

}

```

Store the collapsed state in `localStorage` so it persists across page loads.

Data Architecture: The Single Data File Pattern

The cleanest approach for dashboard data is a single data file that exports everything the dashboard needs. This keeps your mock data organized and makes it easy to swap in a real API later.

```ts

// data/dashboard.ts

export const stats = [

{ label: "Total Revenue", value: "$48,295", change: 12.5, changeLabel: "vs last month" },

{ label: "Active Users", value: "2,847", change: 8.2, changeLabel: "vs last month" },

{ label: "Conversion Rate", value: "3.4%", change: -0.6, changeLabel: "vs last month" },

{ label: "Churn Rate", value: "1.2%", change: -0.3, changeLabel: "vs last month" },

];

export const revenueData = [

{ date: "Jan", revenue: 32000 },

{ date: "Feb", revenue: 38000 },

// ...

];

export const recentOrders = [

{ id: "ORD-001", customer: "Alice Johnson", amount: "$299", status: "Paid" },

// ...

];

```

This pattern works for templates, prototypes, and early-stage products. When you're ready to connect a real API, replace the imports with `fetch` calls — the components don't need to change.

Dark Mode Implementation

Dashboard users are almost universally power users who prefer dark mode. Implement it with CSS variables so the switch is instant and doesn't cause a flash of wrong theme.

```css

/* globals.css */

@import "tailwindcss";

@theme inline {

--color-background: #ffffff;

--color-foreground: #0f172a;

--color-card: #f8fafc;

--color-border: #e2e8f0;

}

.dark {

--color-background: #0f172a;

--color-foreground: #f8fafc;

--color-card: #1e293b;

--color-border: #334155;

}

```

Store the preference in `localStorage` and apply the `.dark` class to `<html>` before the first paint to avoid flicker:

```tsx

// app/layout.tsx — inline script runs before React hydrates

const themeScript = `

const theme = localStorage.getItem("theme") ?? "dark";

document.documentElement.classList.toggle("dark", theme === "dark");

`;

export default function RootLayout({ children }: { children: React.ReactNode }) {

return (

<html lang="en" suppressHydrationWarning>

<head>

<script dangerouslySetInnerHTML={{ __html: themeScript }} />

</head>

<body>{children}</body>

</html>

);

}

```

Responsive Sidebar Patterns

The sidebar needs fundamentally different behavior at different breakpoints:

**Large screens (lg+):** Persistent sidebar. The main content shifts right when the sidebar is open. Use CSS Grid or Flexbox with a fixed-width sidebar column.

**Medium screens (md):** Icon-only sidebar. Keep navigation accessible without consuming too much horizontal space.

**Small screens (sm and below):** Off-canvas drawer. The sidebar slides in from the left over the content. A backdrop overlay closes it when tapped.

```tsx

// Use Tailwind's responsive prefixes to handle this declaratively

<div className="grid grid-cols-1 lg:grid-cols-[240px_1fr]">

<Sidebar />

<main>{children}</main>

</div>

```

Getting Started Fast

Building a production-quality admin dashboard from scratch takes weeks. You need to get every component right, handle responsive behavior at every breakpoint, wire up dark mode, and make sure charts look good in both themes.

Craftly's Dashboard template (coming soon) ships with all of this pre-built: stat cards, Recharts integration, a sortable data table, responsive sidebar with collapse state, and full dark mode. The single data file pattern means you can customize the content without touching the component code.

In the meantime, the patterns above give you everything you need to build your own. Start with the layout and sidebar — get those right and the rest of the components slot in cleanly.

A great admin dashboard isn't about the number of charts or the complexity of the data. It's about making the right numbers immediately visible and actionable. Focus on that, and the technical decisions become clearer.

Browse [Craftly's template collection](https://getcraftly.gumroad.com) for production-ready Next.js templates built with these patterns from the ground up.

Ready to ship faster?

Browse our collection of production-ready templates.

Browse templates