| |

React 72 ⚛️ Tailwind CSS Setup in React

⏱️ Estimated read: 11 min | |

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

StepCommand
Scaffold Reactnpm create vite@latest app -- --template react
Install Tailwindnpm install tailwindcss @tailwindcss/vite
Add pluginvite.config.js: plugins: [react(), tailwindcss()]
Import CSSsrc/index.css: @import "tailwindcss";

What Changed from v3

v3v4
tailwind.config.js@theme in CSS
postcss.config.jsNot needed with Vite plugin
@tailwind base/components/utilities@import "tailwindcss"
Manual content pathsAutomatic detection
!flexflex!

Common Utility Classes

ClassCSS Property
p-4padding: 1rem
bg-whitebackground-color: white
rounded-xlborder-radius
text-centertext-align: center
hover:bg-blue-600Hover state background
md:flexFlex at md breakpoint

Responsive Breakpoints

PrefixMin 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

PitfallWhy It HappensFix
Styles not appliedMissing @import "tailwindcss"Add to main CSS
@theme ignoredCreated tailwind.config.jsDelete it; use CSS @theme
Duplicate CSS outputBoth Vite and PostCSS plugins activeRemove Tailwind from PostCSS
@tailwind directives ignoredv3 syntaxReplace with @import "tailwindcss"
Plugin not foundNot installed or not importednpm install @tailwindcss/vite
important not workingUsed v3 !flex syntaxUse 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

ItemValue
Required packagestailwindcss, @tailwindcss/vite
Vite plugintailwindcss() in plugins
CSS entry@import "tailwindcss";
Config fileNone (CSS @theme instead)
PostCSS configNone
Content pathsAutomatic detection
Custom theme@theme { --color-*: value; }
Important modifierclass! (suffix)
Responsive prefixmd:, lg:, etc.
State prefixhover:, focus:

Key takeaways:

  • Tailwind v4 in React requires only the Vite plugin. No postcss.config.js, no tailwind.config.js, and no @tailwind directives .
  • The setup is four steps. Scaffold Vite + React, install tailwindcss and @tailwindcss/vite, add the plugin to vite.config.js, and import Tailwind in the CSS file.
  • Configuration is CSS-first. Custom colors, fonts, and spacing go in an @theme block, 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.js is ignored by the Vite plugin. A postcss.config.js with 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!