Back to blog
Dev Education
10 min read
Shadcn UI
Next.js 15

How to Set Up Shadcn UI in a Next.js 15 Project (2026 Guide)

Step-by-step guide to installing and configuring Shadcn UI in a fresh Next.js 15 project. Covers project creation, Shadcn initialization, Tailwind config, adding components, theming, and project structure.

SB

SERP Blocks Team

Product

How to Set Up Shadcn UI in a Next.js 15 Project (2026 Guide) cover

Starting a new Next.js project and want to use Shadcn UI for your component library? This guide walks through every step — from creating the project to adding your first components and customizing the theme. No prior Shadcn experience required.

By the end, you'll have a working Next.js 15 project with Shadcn UI fully configured, a handful of components installed, and a clear understanding of how the system works so you can build on it confidently.

Prerequisites

Before starting, make sure you have the following installed:

  • Node.js 18.18 or later — check with node -v

  • A package manager — npm, yarn, pnpm, or bun all work. This guide uses npm, but every command has equivalents for other managers

  • A code editor — VS Code with the Tailwind CSS IntelliSense extension is the recommended setup

If you're running Node 20 or later (the current LTS as of 2026), you're good to go.

Step 1: Create a New Next.js 15 Project

Open your terminal and run:

npx create-next-app@latest my-shadcn-app

The CLI will ask you a series of questions. Here are the recommended answers for a Shadcn UI project:

Would you like to use TypeScript? Yes
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? Yes
Would you like to use Turbopack for next dev? Yes
Would you like to customize the import alias? No

The important ones are TypeScript (Shadcn components are written in TypeScript), Tailwind CSS (required — Shadcn UI is styled entirely with Tailwind), and App Router (the standard routing approach in Next.js 15).

After the CLI finishes, move into the project directory:

cd my-shadcn-app

Verify everything works by starting the dev server:

npm run dev

Open http://localhost:3000 and you should see the default Next.js welcome page. Stop the server with Ctrl+C once you've confirmed it works.

Step 2: Initialize Shadcn UI

With your Next.js project created, run the Shadcn CLI:

npx shadcn@latest init

This command detects your project configuration (TypeScript, Tailwind, App Router) and asks a few setup questions:

Which style would you like to use? New York
Which color would you like to use as the base color? Neutral
Would you like to use CSS variables for theming? Yes

Style — Shadcn offers two visual styles: "Default" and "New York." New York has slightly more refined spacing and borders. Both are fully customizable after setup, so pick whichever appeals to you. New York is the more popular choice in 2026.

Base color — This sets the neutral palette (backgrounds, borders, text). Options include Slate, Gray, Zinc, Neutral, and Stone. They're all close variations of gray. Neutral is a safe default that works with any accent color.

CSS variables — Always say yes. CSS variables make theming dramatically easier and enable dark mode with a single class toggle.

After initialization, the CLI creates and modifies several files:

What Changed in Your Project

components.json — This is Shadcn's configuration file. It tells the CLI where to put components, which style to use, and how imports are aliased. You rarely need to edit this manually.

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "src/app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  }
}

src/app/globals.css — Updated with CSS custom properties for the color theme. You'll see variables like --background, --foreground, --primary, --secondary, --accent, --muted, --destructive, and more — each defined for both light and dark mode.

src/lib/utils.ts — A utility file with the cn() function, which merges Tailwind classes using clsx and tailwind-merge:

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

You'll use cn() constantly when working with Shadcn components. It lets you conditionally apply classes without worrying about Tailwind class conflicts.

src/components/ui/ — This directory is created (empty for now). Every Shadcn component you add will be placed here.

Step 3: Add Your First Components

Shadcn UI doesn't install all components at once. You add only what you need, when you need it. This keeps your bundle lean and your components/ui directory manageable.

Let's add a few foundational components:

npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add input
npx shadcn@latest add label

Or add multiple at once:

npx shadcn@latest add button card input label

Each command copies the component source code into src/components/ui/. After running these, your directory looks like:

src/
  components/
    ui/
      button.tsx
      card.tsx
      input.tsx
      label.tsx
  lib/
    utils.ts

These are real .tsx files in your project — not imported from node_modules. Open button.tsx and you'll see the full component source, built on class-variance-authority for variant management and the cn() utility for class merging. You can read, understand, and modify every line.

Step 4: Use a Component

Let's replace the default Next.js homepage with something that uses Shadcn components. Open src/app/page.tsx and replace its contents:

import { Button } from "@/components/ui/button";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export default function Home() {
  return (
    <main className="flex min-h-screen items-center justify-center p-4">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle>Welcome</CardTitle>
          <CardDescription>
            Your Next.js + Shadcn UI project is ready.
          </CardDescription>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="space-y-2">
            <Label htmlFor="email">Email</Label>
            <Input id="email" type="email" placeholder="you@example.com" />
          </div>
          <Button className="w-full">Get Started</Button>
        </CardContent>
      </Card>
    </main>
  );
}

Start the dev server again with npm run dev and you'll see a centered card with an email input and a button. Clean, accessible, and styled with your Tailwind theme.

Step 5: Understand the Project Structure

Here's how a typical Next.js + Shadcn UI project is organized:

src/
  app/
    layout.tsx          # Root layout with fonts, metadata, theme provider
    page.tsx            # Homepage
    globals.css         # Tailwind base styles + CSS variables
  components/
    ui/                 # Shadcn UI primitives (button, card, dialog, etc.)
    [feature]/          # Your custom components that use the primitives
  lib/
    utils.ts            # cn() utility and other shared helpers
  hooks/                # Custom React hooks

The separation between components/ui (Shadcn primitives) and your custom components is important. The ui directory contains the building blocks — buttons, inputs, dialogs, dropdowns. Your feature components compose those primitives into larger, application-specific UI.

For example, a components/contact-form.tsx might import Button, Input, Textarea, and Select from components/ui/ and compose them into a complete contact form with your specific fields and validation.

Step 6: Set Up Dark Mode

Shadcn UI supports dark mode through CSS variables, but you need a theme provider to toggle between modes. The most common approach uses next-themes:

npm install next-themes

Create a theme provider component:

// src/components/theme-provider.tsx
"use client";

import { ThemeProvider as NextThemesProvider } from "next-themes";

export function ThemeProvider({
  children,
  ...props
}: React.ComponentProps<typeof NextThemesProvider>) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

Wrap your app layout with the provider:

// src/app/layout.tsx
import { ThemeProvider } from "@/components/theme-provider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Now add a theme toggle button. First install the dropdown menu component:

npx shadcn@latest add dropdown-menu

Then create the toggle:

// src/components/theme-toggle.tsx
"use client";

import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export function ThemeToggle() {
  const { setTheme } = useTheme();

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon">
          <Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span className="sr-only">Toggle theme</span>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Every Shadcn component respects the CSS variables, so switching between light and dark mode updates your entire UI instantly — no extra configuration per component.

Step 7: Customize the Theme

The CSS variables in globals.css control the entire visual identity of your project. Here's what the key variables do:

VariablePurpose
--backgroundPage background color
--foregroundDefault text color
--primaryPrimary action color (buttons, links)
--primary-foregroundText color on primary backgrounds
--secondarySecondary/subtle action color
--mutedMuted backgrounds (disabled states, subtle sections)
--accentAccent highlights (hover states, active items)
--destructiveError/danger color (delete buttons, error messages)
--borderDefault border color
--ringFocus ring color
--radiusGlobal border radius

To change your theme, you only need to update these variables. For example, to use a blue primary color instead of the default, update the --primary values in your CSS:

@layer base {
  :root {
    --primary: 221.2 83.2% 53.3%;
    --primary-foreground: 210 40% 98%;
  }
  .dark {
    --primary: 217.2 91.2% 59.8%;
    --primary-foreground: 222.2 84% 4.9%;
  }
}

The Shadcn UI Themes page provides a visual theme builder where you can pick colors and copy the generated CSS variables directly into your project.

Step 8: Add More Components as Needed

Shadcn UI offers over 50 primitive components. Here are the ones you're most likely to need early on, grouped by use case:

Layout and Navigation:

npx shadcn@latest add navigation-menu sheet separator

Forms:

npx shadcn@latest add form select textarea checkbox radio-group switch

Feedback:

npx shadcn@latest add alert dialog sonner tooltip

Data Display:

npx shadcn@latest add table badge avatar tabs accordion

Remember — only add what you need. Each component is independent. Adding dialog doesn't require adding button first (though you'll almost certainly want both). The CLI handles peer dependencies between Shadcn components automatically.

Common Issues and Fixes

"Module not found" After Adding a Component

If you see import errors after running npx shadcn@latest add, make sure your tsconfig.json has the correct path aliases:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

This should be set up automatically by create-next-app, but it's worth verifying if imports break.

Tailwind Classes Not Applying

Make sure your tailwind.config.ts includes the correct content paths. After Shadcn initialization, it should include:

content: [
  "./src/**/*.{js,ts,jsx,tsx,mdx}",
],

If you've moved components to a non-standard directory, update this array to match.

Components Look Unstyled

Check that globals.css is imported in your root layout (src/app/layout.tsx). The CSS variable definitions live there, and without them, all the Tailwind utility classes like bg-primary and text-muted-foreground resolve to nothing.

Dark Mode Not Working

Verify three things:

  1. next-themes is installed and the ThemeProvider wraps your layout

  2. The ThemeProvider uses attribute="class" (not attribute="data-theme")

  3. Your html tag has suppressHydrationWarning to avoid Next.js hydration errors

What Comes After Setup

Once your project is configured and you're comfortable adding components, the next step is building actual page sections — hero areas, feature grids, navigation bars, footers, forms, and everything else that makes up a real website.

This is where the gap between "components" and "blocks" becomes obvious. Shadcn UI gives you the primitives: Button, Card, Input, Dialog. But it doesn't give you a complete hero section with a headline, subtext, CTA buttons, and a background image. It doesn't give you a pricing table with three tiers and a toggle between monthly and annual billing. It doesn't give you a footer with columns of links, social icons, and a newsletter signup.

Building those page sections from Shadcn primitives is entirely possible — that's what the primitives are for. But it takes time, and most projects need dozens of these sections.

SERP Blocks is a library of over 1,200 pre-built page sections — all built with Shadcn UI and Tailwind CSS, designed to drop directly into the kind of Next.js project you just set up. The library covers 50+ categories: 81 hero sections, 132 feature sections, 110 card components, 21 pricing pages, 17 navbar designs, 21 footer layouts, 33 contact forms, 20 dashboard layouts, and dozens more categories.

Each block is a self-contained React component built with the same Shadcn UI components you just installed — Button, Card, Input, Badge, Avatar, and so on. Because they use the same CSS variable theming system, they automatically match your project's color scheme and dark mode settings. Copy a block into your project, and it works immediately with your existing Shadcn setup.

60 blocks are free. The full Pro library is a one-time purchase — no subscription — with lifetime access to every current block and every block added in the future.

Quick Reference

Here's the complete setup sequence in one place for easy reference:

# 1. Create Next.js project
npx create-next-app@latest my-shadcn-app
cd my-shadcn-app

# 2. Initialize Shadcn UI
npx shadcn@latest init

# 3. Add components you need
npx shadcn@latest add button card input label

# 4. Install dark mode support
npm install next-themes

# 5. Start building
npm run dev

Five commands and you're ready to build. The entire setup takes under five minutes on a decent internet connection. From here, you have a production-ready foundation — TypeScript, Tailwind CSS, accessible components, dark mode, and a theming system that scales from a side project to a full SaaS application.