Tailwind with React

className, Conditional Classes, Setup

Tailwind with React

Wire Tailwind v4 into a React and Vite app and manage conditional class names cleanly without breaking content detection.

4 min read Level 2/5 #tailwind#react#vite
What you'll learn
  • Add the @tailwindcss/vite plugin to a React app
  • Compose classes with template literals or clsx
  • Avoid dynamic class string pitfalls

React uses className instead of class, but Tailwind v4 setup is otherwise the same lightweight story: one Vite plugin and one CSS import.

Setup in a Vite React App

Add the first-party Vite plugin and import Tailwind from your CSS entry. No tailwind.config.js is required in v4.

npm install tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [react(), tailwindcss()],
});
/* src/index.css */
@import "tailwindcss";

Conditional Classes

Build conditional class names with a template literal or, for anything non-trivial, the clsx helper:

function Tab({ active }) {
  return (
    <button className={`px-4 py-2 ${active ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-700'}`}>
      Tab
    </button>
  );
}

The Dynamic Class Trap

Tailwind detects classes by scanning source text for complete class names. A string assembled from fragments is never found, so the CSS is never generated:

// BROKEN: `bg-${color}-500` is never seen by the scanner
<div className={`bg-${color}-500`} />

// GOOD: full class names in a lookup map
const bg = { red: 'bg-red-500', blue: 'bg-blue-500' };
<div className={bg[color]} />

Always write complete class strings or map known keys to complete strings.

Tailwind with Vue & Svelte →