Setting Up Tailwind CSS v4 + shadcn/ui in Next.js from Scratch
Uses pnpm or npm with TypeScript.
1. Create Project
Bash
npx create-next-app@latest my-project --typescript --eslint --app
cd my-project
When
create-next-appasks about Tailwind CSS, select No. We will set up v4 manually. This guide uses asrc/directory, so answer Yes when it asks about usingsrc/.
2. Install Packages
Tailwind CSS v4 (PostCSS Method)
Next.js uses the PostCSS plugin instead of the Vite plugin.
pnpm:
Bash
pnpm add tailwindcss @tailwindcss/postcss postcss
npm:
Bash
npm install tailwindcss @tailwindcss/postcss postcss
shadcn/ui Required Dependencies
pnpm:
Bash
pnpm add class-variance-authority clsx tailwind-merge lucide-react
npm:
Bash
npm install class-variance-authority clsx tailwind-merge lucide-react
Node.js Type Definitions
pnpm:
Bash
pnpm add -D @types/node
npm:
Bash
npm install -D @types/node
3. Create Files
postcss.config.mjs
Tauri uses the Vite plugin (@tailwindcss/vite), but Next.js uses the PostCSS plugin.
JAVASCRIPT
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
src/app/globals.css
Based on the Next.js App Router, write this in src/app/globals.css.
CSS
@import "tailwindcss";
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.1450 0 0);
/* ... other CSS variables */
}
.dark {
--background: oklch(0.1450 0 0);
--foreground: oklch(0.9850 0 0);
/* ... dark mode variables */
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
/* ... theme variable mappings */
}
src/lib/utils.ts
TYPESCRIPT
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
components.json
Differences from the Tauri setup: rsc is true, and the CSS path is src/app/globals.css.
JSON
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
4. Modify Files
tsconfig.json
JSON
{
"compilerOptions": {
// ... existing settings
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
If created with
create-next-app, the@/*alias may already be configured.
src/app/layout.tsx
TYPESCRIPT
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "My App",
description: "Next.js + Tailwind CSS v4 + shadcn/ui",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
src/app/page.tsx
TYPESCRIPT
export default function Home() {
return (
<div className="flex items-center justify-center min-h-screen">
<h1 className="text-4xl font-bold">Hello, Next.js + Tailwind v4</h1>
</div>
);
}
5. Adding shadcn/ui Components
Bash
npx shadcn@latest add button
npx shadcn@latest add card
# Add any components you need...
Or initialize everything at once with shadcn init:
Bash
npx shadcn@latest init
Components are automatically generated in the src/components/ui/ folder.
6. Differences from Tauri Setup
| Item | Tauri (Vite) | Next.js |
|---|---|---|
| Tailwind Integration | @tailwindcss/vite (Vite plugin) | @tailwindcss/postcss (PostCSS plugin) |
| Config File | Add plugin in vite.config.ts | Create separate postcss.config.mjs |
| CSS File Location | src/index.css | src/app/globals.css |
| Entry Point | Import CSS in src/main.tsx | Import CSS in src/app/layout.tsx |
| RSC Support | false (client-only) | true (React Server Components) |
tailwind.config.js | Not required (v4) | Not required (v4) |
7. Key Features
- Tailwind CSS v4: CSS-first configuration, no
tailwind.config.jsneeded - PostCSS Integration: Processed as a PostCSS plugin during the Next.js build
- Path Alias: Use
@/*as shorthand forsrc/*paths - Theme System: CSS variable-based, light/dark mode support via
@theme inline - shadcn/ui: Configured for RSC (
rsc: true), ready to add components - App Router: Based on the Next.js App Router