ยท 12 min read

How to Build a Dashboard with Next.js and Tailwind

Step-by-step tutorial on creating a production-ready dashboard from scratch using Next.js 14 and Tailwind CSS.

Next.js Tailwind CSS Tutorial Dashboard

Introduction

Building a dashboard from scratch is one of the best ways to level up your Next.js skills. In this tutorial, we'll walk through creating a production-ready admin dashboard using Next.js 14 and Tailwind CSS.

By the end, you'll have a working dashboard with a sidebar navigation, responsive layout, dark mode, and basic data visualization.

Prerequisites

  • Node.js 18+ installed
  • Basic knowledge of React and TypeScript
  • Familiarity with Tailwind CSS
  • Step 1: Project Setup

    Start by creating a new Next.js project with TypeScript and Tailwind CSS:

    npx create-next-app@14 my-dashboard --typescript --tailwind --app
    cd my-dashboard

    Step 2: Create the Layout Structure

    The foundation of any dashboard is its layout. We'll create a sidebar + main content structure:

    // src/app/dashboard/layout.tsx
    export default function DashboardLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <div className="flex h-screen bg-gray-100 dark:bg-gray-900">
          <Sidebar />
          <main className="flex-1 overflow-y-auto p-6">
            {children}
          </main>
        </div>
      );
    }

    Step 3: Build the Sidebar

    The sidebar is arguably the most important UI element in a dashboard. Here's a clean, responsive implementation:

    // src/components/Sidebar.tsx
    "use client";
    
    import { useState } from "react";
    import Link from "next/link";
    import { usePathname } from "next/navigation";
    
    const navItems = [
      { href: "/dashboard", label: "Overview", icon: "๐Ÿ“Š" },
      { href: "/dashboard/analytics", label: "Analytics", icon: "๐Ÿ“ˆ" },
      { href: "/dashboard/users", label: "Users", icon: "๐Ÿ‘ฅ" },
      { href: "/dashboard/settings", label: "Settings", icon: "โš™๏ธ" },
    ];
    
    export function Sidebar() {
      const pathname = usePathname();
      const [collapsed, setCollapsed] = useState(false);
    
      return (
        <aside className={`${collapsed ? "w-16" : "w-64"} 
          bg-white dark:bg-gray-800 border-r border-gray-200 
          dark:border-gray-700 transition-all duration-300`}>
          {/* Navigation items */}
          <nav className="mt-6 px-3 space-y-1">
            {navItems.map((item) => (
              <Link
                key={item.href}
                href={item.href}
                className={`flex items-center gap-3 px-3 py-2 rounded-lg
                  ${pathname === item.href 
                    ? "bg-blue-50 text-blue-600 dark:bg-blue-900/20" 
                    : "text-gray-600 hover:bg-gray-50 dark:text-gray-300"
                  }`}
              >
                <span>{item.icon}</span>
                {!collapsed && <span>{item.label}</span>}
              </Link>
            ))}
          </nav>
        </aside>
      );
    }

    Step 4: Add KPI Cards

    Every dashboard needs KPI cards to show key metrics at a glance:

    // src/components/KPICard.tsx
    interface KPICardProps {
      title: string;
      value: string;
      change: string;
      positive: boolean;
    }
    
    export function KPICard({ title, value, change, positive }: KPICardProps) {
      return (
        <div className="bg-white dark:bg-gray-800 rounded-xl p-6 
          border border-gray-200 dark:border-gray-700">
          <p className="text-sm text-gray-500 dark:text-gray-400">{title}</p>
          <p className="text-2xl font-bold mt-1">{value}</p>
          <p className={`text-sm mt-2 ${positive ? "text-green-600" : "text-red-600"}`}>
            {change}
          </p>
        </div>
      );
    }

    Step 5: Implement Dark Mode

    Tailwind CSS makes dark mode straightforward. Add the darkMode: "class" config and toggle it:

    // src/contexts/ThemeContext.tsx
    "use client";
    
    import { createContext, useContext, useEffect, useState } from "react";
    
    const ThemeContext = createContext({ dark: false, toggle: () => {} });
    
    export function ThemeProvider({ children }: { children: React.ReactNode }) {
      const [dark, setDark] = useState(false);
    
      useEffect(() => {
        const stored = localStorage.getItem("theme");
        const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
        setDark(stored === "dark" || (!stored && prefersDark));
      }, []);
    
      useEffect(() => {
        document.documentElement.classList.toggle("dark", dark);
        localStorage.setItem("theme", dark ? "dark" : "light");
      }, [dark]);
    
      return (
        <ThemeContext.Provider value={{ dark, toggle: () => setDark(!dark) }}>
          {children}
        </ThemeContext.Provider>
      );
    }
    
    export const useTheme = () => useContext(ThemeContext);

    Step 6: Add Charts with Recharts

    Install Recharts for data visualization:

    npm install recharts

    Then create a basic area chart:

    "use client";
    
    import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
    
    const data = [
      { month: "Jan", revenue: 4000 },
      { month: "Feb", revenue: 3000 },
      { month: "Mar", revenue: 5000 },
      { month: "Apr", revenue: 4500 },
      { month: "May", revenue: 6000 },
      { month: "Jun", revenue: 5500 },
    ];
    
    export function RevenueChart() {
      return (
        <div className="bg-white dark:bg-gray-800 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
          <h3 className="font-semibold mb-4">Revenue Overview</h3>
          <ResponsiveContainer width="100%" height={300}>
            <AreaChart data={data}>
              <CartesianGrid strokeDasharray="3 3" />
              <XAxis dataKey="month" />
              <YAxis />
              <Tooltip />
              <Area type="monotone" dataKey="revenue" stroke="#3b82f6" fill="#3b82f6" fillOpacity={0.1} />
            </AreaChart>
          </ResponsiveContainer>
        </div>
      );
    }

    Step 7: Make It Responsive

    Use Tailwind's responsive utilities to create a mobile-friendly layout:

    // Mobile: stack vertically, Desktop: side by side
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
      <KPICard title="Revenue" value="$45,231" change="+20.1%" positive />
      <KPICard title="Users" value="2,338" change="+180" positive />
      <KPICard title="Orders" value="1,234" change="-3.2%" positive={false} />
      <KPICard title="Conversion" value="3.24%" change="+0.5%" positive />
    </div>

    Step 8: Deploy

    Next.js dashboards deploy seamlessly to Vercel:

    npx vercel

    Or build for self-hosting:

    npm run build
    npm start

    Going Further

    This tutorial covers the basics, but a production dashboard needs more:

  • Authentication โ€” Protect routes with NextAuth or a custom auth system
  • Data fetching โ€” Connect to real APIs with Server Components
  • State management โ€” Use React Query or SWR for server state
  • Tables โ€” Add sortable, filterable data tables
  • File uploads โ€” Implement a file manager
  • If you want all of this pre-built and ready to go, check out Kaiforge, which includes 30+ pages with all these features and more.

    Conclusion

    Building a dashboard with Next.js 14 and Tailwind CSS is surprisingly straightforward. The combination of Server Components for data fetching, Tailwind for styling, and Recharts for visualization gives you a powerful, performant foundation.

    Start simple, iterate fast, and ship something your users will love.

    Ready to build your dashboard?

    Kaiforge gives you 30+ pages, 5 color themes, and everything you need to ship fast.

    Explore Kaiforge