Logo jitendra.dev
Published on

Build Dynamic Themes in Next.js with Tailwind & Shadcn UI

Authors

Table of contents:

Build Dynamic Themes in Next.js with Tailwind & Shadcn UI

Build Dynamic Themes in Next.js with Tailwind & Shadcn UI

🚀 Build beautiful and dynamic multi-theme UI in Next.js using Tailwind CSS and Shadcn UI with full light/dark mode support. This guide walks you through everything from scratch — no prior experience needed!


✅ What We’ll Cover

  • How to create a new Next.js project
  • Install and configure Tailwind CSS
  • Add Shadcn UI components
  • Setup Multiple Light/Dark Themes
  • Use next-themes for theme switching
  • Build a working theme switcher buttons

📁 1. Create a New Next.js App

We’ll use the official create-next-app tool to scaffold the project:

npx create-next-app@latest

You’ll be prompted with a few questions:

 What is your project named?  my-app
 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? (recommended) … Yes
 Would you like to use Turbopack for `next dev`? … No
 Would you like to customize the default import alias?  Yes
 What import alias would you like configured?  @/*

📦 Your folder structure will now include Tailwind and App Router setup out of the box.


🧱 2. Initialize Shadcn UI

Shadcn UI gives you beautiful, accessible components that work perfectly with Tailwind.

Run the init command:

npx shadcn@latest init

You’ll be prompted to set up Shadcn in your project. Accept the defaults or adjust as needed.

🧩 Add First Shadcn Component

Let’s install the button component to verify setup:

npx shadcn@latest add button

✅ You’ll now see Shadcn components appear inside your components/ui/ folder.


🎨 3. Setup Multiple Custom Themes (Light + Dark)

We’ll now define multiple theme options like:

  • theme-blue-light, theme-blue-dark
  • theme-red-light, theme-red-dark

We’ll use CSS variables, Tailwind tokens, and next-themes for switching.

📦 Install next-themes

npm install next-themes

🪄 4. Add Global Theme Styles

Inside your globals.css (or create a new file and import it), define theme tokens like this:

.theme-red-light {
	--radius: 0.625rem;
	--background: oklch(1  0  0); /* white */
	--foreground: oklch(0.15  0.03  30); /* dark grey text */
	--card: oklch(0.98  0.005  20);
	--card-foreground: oklch(0.15  0.03  30);
	--popover: oklch(1  0  0);
	--popover-foreground: oklch(0.15  0.03  30);
	--primary: oklch(0.55  0.25  30); /* rich red */
	--primary-foreground: oklch(1  0  0); /* white text on red */
	--secondary: oklch(0.93  0.03  20); /* light red tint */
	--secondary-foreground: oklch(0.25  0.05  30);
	--accent: oklch(0.93  0.03  20);
	--accent-foreground: oklch(0.2  0.05  30);
	--muted: oklch(0.95  0.005  20);
	--muted-foreground: oklch(0.45  0.01  30);
	--border: oklch(0.9  0.01  20);
	--input: oklch(0.9  0.01  20);
	--ring: oklch(0.55  0.25  30);
	--destructive: oklch(0.6  0.28  27);
	--destructive-foreground: oklch(1  0  0);
}

.theme-red-dark {
	--background: oklch(0.15  0.03  30);
	--foreground: oklch(0.98  0.01  20);
	--card: oklch(0.2  0.02  30);
	--card-foreground: oklch(1  0  0);
	--popover: oklch(0.2  0.02  30);
	--popover-foreground: oklch(1  0  0);
	--primary: oklch(0.65  0.25  30);
	--primary-foreground: oklch(1  0  0);
	--secondary: oklch(0.4  0.05  20);
	--secondary-foreground: oklch(0.98  0.01  20);
	--accent: oklch(0.4  0.05  20);
	--accent-foreground: oklch(1  0  0);
	--muted: oklch(0.3  0.01  20);
	--muted-foreground: oklch(0.7  0.02  20);
	--border: oklch(0.25  0.01  20);
	--input: oklch(0.25  0.01  20);
	--ring: oklch(0.65  0.25  30);
	--destructive: oklch(0.65  0.25  30);
	--destructive-foreground: oklch(1  0  0);
}

/* Add more themes like blue, green, etc. */

⚙️ 5. Tailwind Theme Tokens Setup

In your tailwind.config.ts, extend colors with CSS variable references:

theme: {
  extend: {
    colors: {
      background: "hsl(var(--background))",
      foreground: "hsl(var(--foreground))",
      primary: "hsl(var(--primary))",
      secondary: "hsl(var(--secondary))",
      accent: "hsl(var(--accent))",
    },
  },
},

This allows you to use classes like bg-background, text-foreground, etc.


💡 6. Theme Provider Setup in Layout

Wrap your app/layout.tsx with a theme provider:


import  type { Metadata } from  "next";
import { Geist, Geist_Mono } from  "next/font/google";
import { ThemeProvider } from  "@/components/theme-provider"
import  "./globals.css";

const  geistSans  =  Geist({
	variable:  "--font-geist-sans",
	subsets: ["latin"],
});

const  geistMono  =  Geist_Mono({
	variable:  "--font-geist-mono",
	subsets: ["latin"],
});

export  const  metadata:  Metadata  = {
	title:  "Next Js Multiple Themes Demo",
	description:  "A demo showcasing multiple themes in Next.js using Tailwind css and shdcn UI."
};

export  default  function  RootLayout({ children,}:  Readonly<{
	children:  React.ReactNode;
	}>) {

	return (

		<html  lang="en"  suppressHydrationWarning >
		<body
			className={`${geistSans.variable}  ${geistMono.variable} antialiased`}>

				<ThemeProvider
					attribute="class"
					defaultTheme="theme-red-light"
					enableSystem={false}
					value={{
						light:  "theme-blue-light",
						dark:  "theme-blue-dark",
						"theme-blue-light":  "theme-blue-light",
						"theme-blue-dark":  "theme-blue-dark",
						"theme-red-light":  "theme-red-light",
						"theme-red-dark":  "theme-red-dark",
					}}
				>
				{children}
				</ThemeProvider>
			</body>
		</html>
	);
}

Create a component components/theme-provider.tsx with a theme provider:

"use client"

import  *  as  React  from  "react"
import { ThemeProvider  as  NextThemesProvider } from  "next-themes"

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

The theme-provider.tsx uses next-themes under the hood.


🧭 7. Create a Theme Switcher Component

Build a dropdown component ThemeSwitcher.tsx using Shadcn’s dropdown menu:

import { useTheme } from "next-themes"
import { DropdownMenu, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"

export function ThemeSwitcher() {
  const { setTheme } = useTheme()

  const themes = [
    { label: "Blue Light", value: "theme-blue-light" },
    { label: "Blue Dark", value: "theme-blue-dark" },
    { label: "Red Light", value: "theme-red-light" },
    { label: "Red Dark", value: "theme-red-dark" },
  ]

  return (
    <div  className="flex flex-wrap gap-2">
			{themes.map((t) => (
				<Button key={t.value} variant={theme === t.value ? "default" : "outline"}
				onClick={() => {
					setTheme(t.value);
				}}
				className="text-xs">
					{t.label}
			</Button>

		))}

	</div>
  )
}

📍 Drop <ThemeSwitcher /> inside your homepage or header.


✅ 8. Final Result: Live Multi-Theme Switching

You’re now able to:

  • Switch between multiple light/dark themes
  • Use Tailwind classes like bg-background, text-primary, etc.
  • Add more themes by simply defining CSS variables and listing them in your config

Try It Yourself

🎉 Want to see it in action?

🔗 Live Demo: next-js-multiple-theme-demo.vercel.app
📦 Source Code: GitHub - palactix/next-js-multiple-theme-demo

Fork, clone, and customize your own multi-theme setup easily!


🧠 Conclusion

Setting up multiple themes in a Next.js app doesn’t have to be hard. With Tailwind CSS, next-themes, and Shadcn UI, it becomes modular, scalable, and extremely flexible.

✅ SEO-Friendly Benefits

  • Fast-loading with Tailwind CSS
  • Multiple themes improve UX and engagement
  • Works with App Router and SSR
  • Mobile-friendly and accessible

If you’re building a SaaS dashboard, portfolio, or developer tool, this pattern is 100% production-ready.

Explore More Topics