Skip to main content

Prerequisites

Before installing themes, ensure you have shadcn/ui set up in your Next.js project. If you haven’t already:
1

Initialize shadcn/ui

npx shadcn@latest init
2

Install required components

The theme system requires these shadcn/ui components:
npx shadcn@latest add dropdown-menu scroll-area button

Installation

1

Install the theme system

Install the complete theme system with all 40+ themes:
npx shadcn@latest add https://tweakcn-picker.vercel.app/r/nextjs/theme-system
This installs:
  • lib/themes-config.ts - Theme configuration and metadata
  • components/providers/theme-provider.tsx - Next.js theme provider
  • components/theme-switcher.tsx - Interactive theme switcher UI
  • styles/themes/*.css - All 40+ theme CSS files
  • next-themes dependency for theme management
2

Add ThemeProvider to your root layout

Wrap your application with the ThemeProvider in app/layout.tsx:
app/layout.tsx
import { ThemeProvider } from "@/components/providers/theme-provider";
import "@/styles/themes/index.css";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}
The suppressHydrationWarning prop prevents hydration warnings from theme switching.
3

Add the ThemeSwitcher to your UI

Import and use the ThemeSwitcher component anywhere in your app:
components/header.tsx
import { ThemeSwitcher } from "@/components/theme-switcher";

export function Header() {
  return (
    <header>
      <nav>
        {/* Your navigation */}
        <ThemeSwitcher />
      </nav>
    </header>
  );
}

How it works

The Next.js adapter uses next-themes for theme management:
Located at components/providers/theme-provider.tsx:
"use client";

import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ReactNode } from "react";
import { allThemeValues, DEFAULT_THEME } from "@/lib/themes-config";

export function ThemeProvider({ children }: { children: ReactNode }) {
  return (
    <NextThemesProvider
      attribute="data-theme"
      themes={allThemeValues}
      defaultTheme={DEFAULT_THEME}
      enableSystem={false}
      disableTransitionOnChange
    >
      {children}
    </NextThemesProvider>
  );
}
Themes are stored as data-theme attributes with values like catppuccin-dark or vercel-light.
All theme metadata is in lib/themes-config.ts:
export interface ThemeConfig {
  name: string;          // e.g., "catppuccin"
  title: string;         // Display name
  primaryLight: string;  // Primary color in light mode
  primaryDark: string;   // Primary color in dark mode
  fontSans: string;      // Font family
}

export const themes: ThemeConfig[] = [
  {
    name: "catppuccin",
    title: "Catppuccin",
    primaryLight: "oklch(0.55 0.25 297.02)",
    primaryDark: "oklch(0.79 0.12 304.77)",
    fontSans: "Montserrat, sans-serif",
  },
  // ... 40+ more themes
];
The ThemeSwitcher component parses theme strings to separate color theme from mode:
function parseTheme(theme: string | undefined): {
  colorTheme: string;
  mode: "light" | "dark";
} {
  if (!theme) return { colorTheme: "default", mode: "dark" };
  
  if (theme.endsWith("-dark")) {
    return { colorTheme: theme.replace("-dark", ""), mode: "dark" };
  }
  if (theme.endsWith("-light")) {
    return { colorTheme: theme.replace("-light", ""), mode: "light" };
  }
  return { colorTheme: "default", mode: "dark" };
}

Adding individual themes

To install specific themes instead of all 40+:
npx shadcn@latest add https://tweakcn-picker.vercel.app/r/theme-catppuccin
Then import only the themes you need:
styles/themes/index.css
@import "./catppuccin.css";
@import "./vercel.css";
@import "./supabase.css";

Customizing themes

All theme CSS files are in styles/themes/. Each theme defines CSS variables:
styles/themes/catppuccin.css
[data-theme="catppuccin-light"] {
  --background: oklch(0.98 0.01 273.65);
  --foreground: oklch(0.30 0.04 274.26);
  --primary: oklch(0.55 0.25 297.02);
  --primary-foreground: oklch(1 0 0);
  /* ... more variables */
}

[data-theme="catppuccin-dark"] {
  --background: oklch(0.24 0.03 266.56);
  --foreground: oklch(0.86 0.02 267.81);
  --primary: oklch(0.79 0.12 304.77);
  /* ... more variables */
}
Edit these files to customize colors, borders, shadows, and more.

TypeScript usage

Use the useTheme hook from next-themes:
import { useTheme } from "next-themes";

function MyComponent() {
  const { theme, setTheme } = useTheme();
  
  return (
    <button onClick={() => setTheme("catppuccin-dark")}>
      Current theme: {theme}
    </button>
  );
}

Next steps

Browse Themes

Explore all 40+ available themes

Theme Picker

Preview themes in real-time

Vite Setup

Install themes in Vite React

Astro Setup

Install themes in Astro

Build docs developers (and LLMs) love