With the release of Next.js 15 and Tailwind CSS v4, setting up a modern web project has never been easier. This guide walks you through setting up Next.js 15 with ShadCN UI and Tailwind CSS v4, leveraging the latest inline theming approach without needing tailwind.config.ts
. We will also integrate dark mode with next-themes
, ensuring a seamless experience.
🔧 Prerequisites
Before starting, ensure you have:
✅ Node.js 18+ installed (Check with node -v
)
✅ npm or pnpm installed
✅ Basic knowledge of Next.js & Tailwind CSS
1️⃣ Create a Next.js 15 App
Run the following command to initialize a TypeScript-based Next.js 15 project:
npx create-next-app@latest my-project --ts --tailwind --eslint --app
🔹 --ts
→ Enables TypeScript
🔹 --tailwind
→ Auto-configures Tailwind CSS
🔹 --app
→ Uses the new App Router (recommended for Next.js 15)
Just follow steps in the terminal menu that will appear like this:
Need to install the following packages:
create-next-app@15.2.1
Ok to proceed? (y) y
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /home/devd/my-project.
Using npm.
Initializing project with template: app-tw
Installing dependencies:
- react
- react-dom
- next
Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- @tailwindcss/postcss
- tailwindcss
- eslint
- eslint-config-next
- @eslint/eslintrc
added 313 packages, and audited 314 packages in 1m
130 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Initialized a git repository.
Success! Created my-project at /home/devd/my-project
2️⃣ Install ShadCN UI
ShadCN provides a collection of beautiful, accessible, and customizable UI components.
npx shadcn-ui@latest init
Then install components (like all if you want)**:
npx shadcn@latest add
~$ cd my-project
~/my-project$ npx shadcn@latest init
Need to install the following packages:
shadcn-ui@0.9.5
Ok to proceed? (y) y
~/my-project$ npx shadcn@latest add
✔ Which components would you like to add? › accordion, alert, alert-dialog, aspect-ratio, avatar, badge, breadcrumb, button, calendar, card, carousel, chart, checkbox, collapsible, command, context-menu, dialog, drawer, dropdown-menu, form, hover-card, input, input-otp, label, menubar, navigation-menu, pagination, popover, progress, radio-group, resizable, scroll-area, select, separator, sheet, sidebar, skeleton, slider, sonner, switch, table, tabs, textarea, toggle, toggle-group, tooltip
✔ You need to create a components.json file to add components. Proceed? … yes
✔ Which color would you like to use as the base color? › Neutral
✔ Writing components.json.
It looks like you are using React 19.
Some packages may fail to install due to peer dependency issues in npm (see https://ui.shadcn.com/react-19).
✔ How would you like to proceed? › Use --legacy-peer-deps
✔ Checking registry.
✔ Updating src/app/globals.css
Installing dependencies.
It looks like you are using React 19.
Some packages may fail to install due to peer dependency issues in npm (see https://ui.shadcn.com/react-19).
✔ How would you like to proceed? › Use --legacy-peer-deps
✔ Installing dependencies.
✔ Created 47 files:
- src/components/ui/accordion.tsx
- src/components/ui/alert.tsx
- src/components/ui/alert-dialog.tsx
- src/components/ui/button.tsx
- src/components/ui/aspect-ratio.tsx
- src/components/ui/avatar.tsx
- src/components/ui/badge.tsx
- src/components/ui/breadcrumb.tsx
- src/components/ui/calendar.tsx
- src/components/ui/card.tsx
- src/components/ui/carousel.tsx
- src/components/ui/chart.tsx
- src/components/ui/checkbox.tsx
- src/components/ui/collapsible.tsx
- src/components/ui/command.tsx
- src/components/ui/dialog.tsx
- src/components/ui/context-menu.tsx
- src/components/ui/drawer.tsx
- src/components/ui/dropdown-menu.tsx
- src/components/ui/form.tsx
- src/components/ui/label.tsx
- src/components/ui/hover-card.tsx
- src/components/ui/input.tsx
- src/components/ui/input-otp.tsx
- src/components/ui/menubar.tsx
- src/components/ui/navigation-menu.tsx
- src/components/ui/pagination.tsx
- src/components/ui/popover.tsx
- src/components/ui/progress.tsx
- src/components/ui/radio-group.tsx
- src/components/ui/resizable.tsx
- src/components/ui/scroll-area.tsx
- src/components/ui/select.tsx
- src/components/ui/separator.tsx
- src/components/ui/sheet.tsx
- src/components/ui/sidebar.tsx
- src/components/ui/tooltip.tsx
- src/hooks/use-mobile.ts
- src/components/ui/skeleton.tsx
- src/components/ui/slider.tsx
- src/components/ui/sonner.tsx
- src/components/ui/switch.tsx
- src/components/ui/table.tsx
- src/components/ui/tabs.tsx
- src/components/ui/textarea.tsx
- src/components/ui/toggle.tsx
- src/components/ui/toggle-group.tsx
3️⃣Configure Tailwind CSS v4 (No Config File Needed!)
Tailwind CSS v4 no longer requires tailwind.config.ts
. Instead, we define variables inside global.css.
Modify app/globals.css
On your existing globals.css file you can add few more color variables like this
//rest of the code
:root {
--background-primary: rgba(249, 250, 251, 1);
--background-secondary: rgba(18, 18, 18, 1);
--text-primary: rgba(55, 65, 81, 1);
--text-secondary: rgba(243, 244, 246, 1);
--buttons: rgba(250, 204, 21, 1);
--border-color: rgba(209, 213, 219, 1);
}
.dark {
--background-primary: rgba(18, 18, 18, 1);
--background-secondary: rgba(249, 250, 251, 1);
--text-primary: rgba(243, 244, 246, 1);
--text-secondary: rgba(55, 65, 81, 1);
}
//rest of the code
Then on the @theme inline
just simply assign those variable with prefix --color-
to anyName
you choose to use inside Tailwind classes in your components:
@import "tailwindcss";
@import "tailwindcss-animate";
@custom-variant dark (&:is(.dark *));
@theme inline {
// rest of the code ......
--color-buttons: var(--buttons);
--color-textSecondary: var(--text-secondary);
--color-textPrimary: var(--text-primary);
--color-backgroundSecondary: var(--background-secondary);
--color-backgroundPrimary: var(--background-primary);
--color-borderColor: var(--border-color);
}
Eg: In your any component, you have this element and you can use the variables inside the tailwind classes as text-textPrimary
<h1 className="text-sm text-textPrimary"> Hello </h1>
🚀 This allows you to use Tailwind CSS variables without a config file!
4️⃣ Enable Dark Mode with next-themes
Install next-themes
npm install next-themes
or
npm install next-themes --legacy-peer-deps
Create theme-provider.tsx
in components/
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [mounted, setMounted] = useState(false);
// Fix hydration issue (ensures the theme is loaded on the client)
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return <>{children}</>; // Prevents mismatch between server & client rendering
}
return (
<NextThemesProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</NextThemesProvider>
);
}
Create theme-toggle.tsx
"use client";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
export function ThemeToggle() {
const { setTheme, resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);
// Fix hydration issue
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null; // Avoids hydration mismatch on SSR
}
return (
<button
className="fixed bottom-4 right-4 p-2 bg-buttons text-textPrimary rounded"
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
>
{resolvedTheme === "dark" ? "Light Mode" : "Dark Mode"}
</button>
);
}
Modify layout.tsx
to Use Theme Provider
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "../../theme/theme-provider";
import { ThemeToggle } from "../../theme/theme-toggle";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<ThemeProvider>
{children}
<ThemeToggle />
</ThemeProvider>
</body>
</html>
);
}
** 5️⃣ Run the Project**
Finally, start the Next.js development server:
npm run dev
Your Next.js 15 + Tailwind v4 + ShadCN UI project is now ready! 🎉
🎯 Why This Setup is Awesome?
✅ No Tailwind Config File Needed (Everything inline in globals.css
)
✅ Modern UI Components with ShadCN
✅ Dark Mode Support with next-themes
✅ Scalable & Performant (Next.js 15 App Router)
🚀 Now you have a fully functional modern Next.js stack with the latest Tailwind CSS v4 features! Let me know if you have any questions or want improvements. Happy coding! 🎨🔥
Top comments (0)