React 72 ⚛️ Tailwind CSS Setup in React
Tailwind CSS v4 changed the setup story completely. In v3, a React project needed tailwind.config.js, postcss.config.js, and three @tailwind directives in the CSS file. In v4, none of that is required. The framework ships a first-party Vite plugin (@tailwindcss/vite) that handles CSS processing natively, eliminates PostCSS from the pipeline, and replaces the configuration file with a CSS-first @theme directive . For a React project scaffolded with Vite—which is the standard path today—the setup is four steps and roughly two minutes.
The reason this matters is not just fewer files. The Vite plugin is faster than the PostCSS approach because it integrates directly with Vite’s module graph and uses Lightning CSS under the hood for optimization . Automatic content detection means you never configure content paths again; Tailwind scans your project files based on heuristics and ignores .gitignore entries automatically . The trade-off is that v4 is opinionated: if you create a tailwind.config.js out of habit, it is ignored. Configuration lives in CSS now.
Key point: Tailwind CSS v4 in a Vite + React project requires only two dependencies (tailwindcss and @tailwindcss/vite), one plugin entry in vite.config.js, and one @import "tailwindcss"; line in your CSS file—no tailwind.config.js, no postcss.config.js, and no @tailwind directives.
Why the v4 setup is simpler than v3
The PostCSS removal. In v3, Tailwind ran as a PostCSS plugin. That meant installing postcss and autoprefixer, creating postcss.config.js, and chaining the processing steps . The Vite plugin in v4 bypasses PostCSS entirely. It hooks into Vite’s transform pipeline and uses Lightning CSS for vendor prefixing and minification, which is faster and requires no configuration . For a standard React + Vite project, you do not need postcss.config.js at all .
The config file removal. v3 required tailwind.config.js with a content array listing every file pattern to scan. v4 uses automatic content detection. It scans your project, respects .gitignore, and ignores binary files. If you need to add a source that is excluded by default—a component library in node_modules, for example—you add an @source directive in your CSS file . Custom theme values go in an @theme block, also in CSS .
The directive removal. v3 used three directives: @tailwind base;, @tailwind components;, and @tailwind utilities;. v4 replaces all three with a single import: @import "tailwindcss"; . The import loads the theme, base styles, components, and utilities in the correct cascade order. If you use @layer components for custom component styles, those blocks still work after the import .
The React plugin order. When adding the Tailwind Vite plugin to a React project, the React plugin and the Tailwind plugin both go in the plugins array. The order does not matter for correctness in a standard setup, but the common convention is to list react() first and tailwindcss() second .
a. The four-step setup for a Vite + React project
The first step is to scaffold the React project if you do not have one. Vite’s React template is the standard starting point:
npm create vite@latest my-tailwind-app -- --template react
cd my-tailwind-app
For TypeScript, use --template react-ts. The Vite scaffold creates a project with src/main.jsx, src/App.jsx, and src/index.css.
The second step is to install the two dependencies. Note that tailwindcss and @tailwindcss/vite are the only packages required. Do not install postcss or autoprefixer for a standard setup:
npm install tailwindcss @tailwindcss/vite
The third step is to register the Vite plugin. Open vite.config.js (or vite.config.ts for TypeScript) and add the Tailwind plugin to the plugins array:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()]
})
The fourth step is to replace the contents of src/index.css with the single Tailwind import:
@import "tailwindcss";
Then verify that src/main.jsx imports this CSS file. The Vite scaffold usually does this already, but if not, add import './index.css' to the entry point. Run npm run dev and you are ready to use utility classes .
b. Using Tailwind utilities in a React component
Once the setup is complete, styling is done through className attributes with utility classes. A card component demonstrates the pattern:
export default function ProfileCard() {
return (
<div className="bg-white p-6 rounded-3xl shadow-xl max-w-xs border border-gray-100">
<img
src="https://github.com/octocat.png"
alt="Profile"
className="w-24 h-24 rounded-full border-4 border-blue-50 mx-auto"
/>
<div className="text-center mt-4">
<h2 className="text-xl font-bold text-gray-800">Octo Cat</h2>
<p className="text-blue-600 font-medium">Front-end Dev</p>
</div>
<button className="w-full bg-blue-600 text-white font-bold py-3 rounded-xl mt-6">
Follow
</button>
</div>
)
}
Each class applies one property: bg-white for background, p-6 for padding, rounded-3xl for border radius, shadow-xl for shadow, and so on. There is no CSS file to name, no class to invent, and no stylesheet to import into the component . The utility names map directly to CSS properties, and responsive variants use prefixes like md: and lg:. Hover states use hover:, and focus states use focus: .
For custom theme values, the v4 pattern is an @theme block in the CSS file. This replaces the theme.extend section of the old JavaScript config:
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-secondary: #64748b;
--font-sans: 'Inter', system-ui, sans-serif;
}
These values become available as utility classes: bg-primary, text-secondary, and font-sans . They are also emitted as CSS variables, so you can reference them in inline styles or other CSS.
c. Common migration pitfalls and v4-specific behavior
The most common mistake is creating tailwind.config.js out of habit. In v4, this file is not read by the Vite plugin. Configuration must go in the CSS file via @theme . A second common mistake is leaving postcss.config.js in place with the tailwindcss plugin. If both the Vite plugin and the PostCSS plugin are active, the CSS can be processed twice, producing duplicate styles or conflicting output . The fix is to remove the Tailwind plugin from PostCSS entirely and rely on the Vite plugin.
The old @tailwind base; directives do not work in v4. If you paste them into a CSS file, they are ignored. The single @import "tailwindcss"; is the only supported entry point . Similarly, the !important modifier moved from the beginning of the class name to the end: v4 uses flex! where v3 used !flex .
For projects that still need PostCSS—because of a plugin that has no Vite equivalent—the Vite plugin and PostCSS can coexist, but the Tailwind plugin must be removed from postcss.config.js. PostCSS will then process only the non-Tailwind CSS, and the Vite plugin handles Tailwind .
Complete Example Session
The following session builds a complete React component with Tailwind v4, using responsive variants, hover states, and custom theme values.
/* src/index.css */
@import "tailwindcss";
@theme {
--color-brand: #6366f1;
--color-brand-dark: #4f46e5;
}
// src/App.jsx
export default function App() {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl shadow-lg p-8 max-w-md w-full">
<h1 className="text-2xl font-bold text-gray-900 mb-2">
Tailwind v4 in React
</h1>
<p className="text-gray-600 mb-6">
No config files. No PostCSS. Just utilities.
</p>
<div className="flex gap-2 mb-6">
<span className="px-3 py-1 bg-brand/10 text-brand rounded-full text-sm font-medium">
Vite
</span>
<span className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm font-medium">
React
</span>
</div>
<button className="w-full bg-brand hover:bg-brand-dark text-white font-semibold py-3 rounded-xl transition-colors">
Get Started
</button>
<p className="text-xs text-gray-400 text-center mt-4 md:text-sm">
Resize the window to see responsive text
</p>
</div>
</div>
)
}
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()]
})
The CSS file imports Tailwind and defines two custom colors under @theme. The component uses bg-brand and hover:bg-brand-dark, which are generated from those theme values. The layout uses min-h-screen and flex items-center justify-center to center the card. The responsive text uses md:text-sm, which applies at the md breakpoint and above. The transition-colors utility adds a smooth hover animation. No CSS module, no stylesheet import in the component, and no tailwind.config.js exist anywhere in the project.
Quick Reference
Setup Commands
| Step | Command |
|---|---|
| Scaffold React | npm create vite@latest app -- --template react |
| Install Tailwind | npm install tailwindcss @tailwindcss/vite |
| Add plugin | vite.config.js: plugins: [react(), tailwindcss()] |
| Import CSS | src/index.css: @import "tailwindcss"; |
What Changed from v3
| v3 | v4 |
|---|---|
tailwind.config.js | @theme in CSS |
postcss.config.js | Not needed with Vite plugin |
@tailwind base/components/utilities | @import "tailwindcss" |
Manual content paths | Automatic detection |
!flex | flex! |
Common Utility Classes
| Class | CSS Property |
|---|---|
p-4 | padding: 1rem |
bg-white | background-color: white |
rounded-xl | border-radius |
text-center | text-align: center |
hover:bg-blue-600 | Hover state background |
md:flex | Flex at md breakpoint |
Responsive Breakpoints
| Prefix | Min Width |
|---|---|
sm: | 640px |
md: | 768px |
lg: | 1024px |
xl: | 1280px |
Best Practices
✅ Do This:
npm install tailwindcss @tailwindcss/vite # ✅ two packages
plugins: [react(), tailwindcss()] # ✅ Vite plugin
@import "tailwindcss"; # ✅ single import
@theme { --color-brand: #6366f1; } # ✅ CSS config
❌ Don’t Do This:
npx tailwindcss init # ❌ creates ignored config
npm install postcss autoprefixer # ❌ not needed
@tailwind base; @tailwind components; @tailwind utilities; # ❌ v3 directives
# leave tailwindcss in postcss.config.js # ❌ double processing
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Styles not applied | Missing @import "tailwindcss" | Add to main CSS |
@theme ignored | Created tailwind.config.js | Delete it; use CSS @theme |
| Duplicate CSS output | Both Vite and PostCSS plugins active | Remove Tailwind from PostCSS |
@tailwind directives ignored | v3 syntax | Replace with @import "tailwindcss" |
| Plugin not found | Not installed or not imported | npm install @tailwindcss/vite |
important not working | Used v3 !flex syntax | Use flex! in v4 |
Real-World Examples
1. Vite config with React and Tailwind
plugins: [react(), tailwindcss()]
2. Single CSS import
@import "tailwindcss";
3. Custom color in @theme
@theme { --color-brand: #6366f1; }
4. Use custom color
<button className="bg-brand">Click</button>
5. Responsive utility
<div className="text-sm md:text-lg">Responsive</div>
6. Hover state
<button className="hover:bg-blue-700">Hover</button>
7. Centering layout
<div className="flex items-center justify-center min-h-screen">
8. Card with shadow
<div className="bg-white p-6 rounded-xl shadow-lg">
9. Source directive for library
@source "../node_modules/@my-company/ui-lib";
10. Disable optimization (debug)
tailwindcss({ optimize: false })
Visual
v3 vs v4 Project Structure
┌──────────────────────────────────────────────┐
│ v3 PROJECT │
│ ├── tailwind.config.js ← content, theme │
│ ├── postcss.config.js ← tailwind, autoprefixer │
│ └── src/index.css ← @tailwind base... │
│ │
│ v4 PROJECT │
│ ├── vite.config.js ← tailwindcss() │
│ └── src/index.css ← @import "tailwindcss"; │
│ ← @theme { ... } │
└──────────────────────────────────────────────┘
The Setup Flow
┌──────────────────────────────────────────────┐
│ 1. npm create vite@latest │
│ │ │
│ ▼ │
│ 2. npm install tailwindcss @tailwindcss/vite│
│ │ │
│ ▼ │
│ 3. vite.config.js: plugins: [tailwindcss()] │
│ │ │
│ ▼ │
│ 4. index.css: @import "tailwindcss"; │
│ │ │
│ ▼ │
│ 5. npm run dev → use className utilities │
└──────────────────────────────────────────────┘
Utility Class to CSS
┌──────────────────────────────────────────────┐
│ <div className="bg-white p-6 rounded-xl"> │
│ │ │
│ ▼ │
│ .bg-white { background-color: #fff; } │
│ .p-6 { padding: 1.5rem; } │
│ .rounded-xl { border-radius: 0.75rem; } │
│ │
│ Each class is a single-purpose utility. │
│ Combine them to build components. │
└──────────────────────────────────────────────┘
v4 Pipeline
┌──────────────────────────────────────────────┐
│ CSS with @import "tailwindcss" │
│ │ │
│ ▼ │
│ @tailwindcss/vite plugin │
│ │ │
│ ▼ │
│ Lightning CSS (optimize, prefix, minify) │
│ │ │
│ ▼ │
│ Output CSS (utilities used in project only) │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Required packages | tailwindcss, @tailwindcss/vite |
| Vite plugin | tailwindcss() in plugins |
| CSS entry | @import "tailwindcss"; |
| Config file | None (CSS @theme instead) |
| PostCSS config | None |
| Content paths | Automatic detection |
| Custom theme | @theme { --color-*: value; } |
| Important modifier | class! (suffix) |
| Responsive prefix | md:, lg:, etc. |
| State prefix | hover:, focus: |
Key takeaways:
- Tailwind v4 in React requires only the Vite plugin. No
postcss.config.js, notailwind.config.js, and no@tailwinddirectives . - The setup is four steps. Scaffold Vite + React, install
tailwindcssand@tailwindcss/vite, add the plugin tovite.config.js, and import Tailwind in the CSS file. - Configuration is CSS-first. Custom colors, fonts, and spacing go in an
@themeblock, not a JavaScript file . - Content detection is automatic. You do not list template paths. Tailwind scans your project based on heuristics and
.gitignore. - Do not create v3 config files. A
tailwind.config.jsis ignored by the Vite plugin. Apostcss.config.jswith Tailwind causes double processing . - The plugin order is flexible.
[react(), tailwindcss()]is the conventional arrangement. - Lightning CSS handles optimization. The Vite plugin uses it for vendor prefixing and minification in production builds .
Remember: Tailwind CSS v4 with Vite and React is the simplest setup the framework has ever had. Two dependencies, one plugin entry, one import line. The complexity has moved into CSS: theme values live in @theme, custom utilities use @utility, and source paths use @source when automatic detection misses something. If you are coming from v3, the migration is mostly deletion—remove the config files, remove the PostCSS setup, replace the three directives with one import. The utilities in your components do not change; bg-white, p-6, and hover:bg-blue-600 work exactly as they did before.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!