Tailwind CSS 2 🎨 Setting Up Tailwind CSS via @tailwindcss/vite and @tailwindcss/cli
In the previous chapter, you compared the utility-first paradigm to the BEM approach and decided whether Tailwind fits your project. This chapter assumes it does. The next question is how to install it. Tailwind CSS v4 offers two official integration paths: the Vite plugin for projects that already use Vite, and the standalone CLI for projects that do not have a build tool or prefer to keep the CSS pipeline separate.
The two paths produce the same output. The difference is the workflow. The Vite plugin hooks into the dev server and the build process, providing hot module replacement and automatic CSS generation. The CLI is a standalone executable that reads a CSS file, scans your source files for class names, and writes the compiled CSS to an output file. It is the simplest way to get started, and it works with any project structure .
Key point: Tailwind CSS v4 requires a different setup than v3. The old @tailwind base; @tailwind components; @tailwind utilities; directives are gone. They are replaced by a single @import "tailwindcss"; statement. The old tailwind.config.js file is also gone for most projects. Configuration moves into the CSS file using the @theme directive. The old PostCSS setup with autoprefixer and postcss-import is no longer required when using the Vite plugin .
Why the Two Paths Exist
Tailwind is not a framework that dictates your build tool. It is a CSS generator that scans your source files for class names and produces a stylesheet. The generator needs two things: a source of class names and a way to run. The Vite plugin and the CLI are two ways to provide both.
The Vite path. Vite is the build tool that most modern React projects use. The @tailwindcss/vite plugin integrates Tailwind into Vite’s build pipeline. When the dev server starts, Tailwind scans the source files and generates the CSS. When a file changes, the plugin regenerates only the affected CSS and pushes the update to the browser via HMR. The setup is minimal: install two packages, add one plugin, import the CSS .
The CLI path. Not every project uses Vite. A static HTML site, a server-rendered application, or a project with a custom build system may not have Vite. The @tailwindcss/cli package provides a standalone executable that compiles Tailwind CSS from the command line. It reads an input CSS file, scans the project for class names, and writes an output CSS file. The watch mode recompiles on file changes. The CLI works with any project structure because it does not depend on a build tool .
The trade-off. The Vite plugin is faster in development because it integrates with the dev server. The CLI is simpler to set up in projects that do not have Vite, but the watch mode requires a separate terminal process. For a Vite project, the plugin is the recommended choice. For everything else, the CLI is the answer .
a. Setting Up Tailwind with the Vite Plugin
The Vite plugin is the recommended integration for Vite projects. It requires two packages: tailwindcss and @tailwindcss/vite. Both are development dependencies .
npm install tailwindcss @tailwindcss/vite
The tailwindcss package contains the compiler and the default theme. The @tailwindcss/vite package contains the Vite plugin. After installation, the plugin is added to the Vite configuration.
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
The plugin is added to the plugins array alongside the React plugin. The order does not matter. The plugin registers three internal sub-plugins that handle scanning, generating in serve mode, and generating in build mode .
The CSS file imports Tailwind with a single statement:
/* src/index.css */
@import "tailwindcss";
This single import replaces the three @tailwind directives from v3. It imports the theme, the preflight base styles, and the utilities in the correct order .
The CSS file must be imported in the application entry point. In a React project, the entry point is main.tsx or main.jsx:
// src/main.tsx
import './index.css'
Once the CSS is imported, the Vite dev server generates the Tailwind styles automatically. When a source file changes and a new utility class appears, the plugin regenerates the CSS and updates the browser. The HMR is fast because the plugin only regenerates the styles for the changed classes .
The Vite plugin has one option: optimize. By default, it is enabled in production builds and disabled in development. The option controls Lightning CSS, which minifies the CSS and adds vendor prefixes. It can be disabled entirely or set to optimize without minifying .
// Disable optimization entirely
tailwindcss({ optimize: false })
// Optimize without minifying
tailwindcss({ optimize: { minify: false } })
The @tailwindcss/vite plugin is available in Tailwind v4.0 and later. It replaces the PostCSS-based approach. When using the plugin, there is no postcss.config.js file and no autoprefixer package. The plugin handles everything .
b. Setting Up Tailwind with the CLI
The CLI is the standalone tool for projects that do not use Vite. It is installed as a development dependency along with tailwindcss.
npm install tailwindcss @tailwindcss/cli
The CLI is invoked with the @tailwindcss/cli package name, not tailwindcss. The old npx tailwindcss command from v3 does not work with v4. It will fail or produce incorrect output .
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch
The -i flag specifies the input CSS file. The -o flag specifies the output CSS file. The --watch flag keeps the process running and recompiles on changes .
The input CSS file contains the Tailwind import:
/* src/input.css */
@import "tailwindcss";
The output CSS file is generated by the CLI. It contains all the utility classes that the CLI found in the source files. The output file must be linked in the HTML:
<link href="./output.css" rel="stylesheet" />
The CLI scans the project automatically. In v4, the content detection is heuristic. It ignores files in .gitignore and skips binary extensions. It can be extended with the @source directive in the CSS file:
@import "tailwindcss";
@source "../node_modules/@my-company/ui-lib";
The @source directive adds a directory to the scan. It is useful for monorepos where the UI library lives in a separate package .
The CLI supports the same optimization options as the Vite plugin. The --minify flag enables minification. The --optimize flag enables optimization without minification. The --map flag generates source maps .
For development, the CLI is run in watch mode:
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch
For production, the CLI is run with minification:
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --minify
The commands are typically added to package.json scripts:
{
"scripts": {
"dev": "tailwindcss -i ./src/input.css -o ./src/output.css --watch",
"build": "tailwindcss -i ./src/input.css -o ./src/output.css --minify"
}
}
The npm run dev command starts the watch mode. The npm run build command produces the minified production CSS .
c. The CSS-First Configuration
Both the Vite plugin and the CLI use the same configuration model: CSS-first. The old tailwind.config.js file is no longer the primary way to configure Tailwind. Instead, the configuration lives in the CSS file using the @theme directive .
@import "tailwindcss";
@theme {
--color-brand: #16a34a;
--color-primary: oklch(0.53 0.12 118.34);
--font-display: "Satoshi", sans-serif;
--breakpoint-3xl: 120rem;
}
Each variable in the @theme block generates a utility class. The --color-brand variable generates bg-brand, text-brand, and border-brand. The --font-display variable generates font-display. The --breakpoint-3xl variable generates the 3xl: variant .
The variables are also available as CSS custom properties. They can be used in inline styles, in other CSS rules, or in JavaScript:
<div style="background-color: var(--color-primary)">
<p class="text-brand font-display">Styled with theme variables</p>
</div>
Custom utilities are defined with the @utility directive:
@utility content-auto {
content-visibility: auto;
}
The @utility directive replaces the @layer utilities pattern from v3. Custom utilities defined this way work with variants like hover: and lg: .
Custom variants are defined with the @variant directive:
@variant hocus (&:hover, &:focus);
The variant can be used as hocus:bg-blue-500 in the markup .
The CSS-first configuration eliminates the tailwind.config.js file for most projects. The only reason to keep a JavaScript config is for legacy plugins or specific requirements. In that case, the @config directive points to the file:
@import "tailwindcss";
@config "./tailwind.config.js";
For new projects, the CSS-first configuration is the recommended approach. It keeps the configuration in the same file as the import, reduces the number of files, and makes the theme variables available as native CSS custom properties .
Complete Example Session
This session demonstrates both the Vite plugin and the CLI setup.
# ============================================
# PART 1: THE VITE PLUGIN SETUP
# ============================================
# Create a Vite React project
npm create vite@latest my-tailwind-app -- --template react
cd my-tailwind-app
# Install Tailwind and the Vite plugin
npm install tailwindcss @tailwindcss/vite
# ============================================
# PART 2: CONFIGURE THE VITE PLUGIN
# ============================================
# vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
# ============================================
# PART 3: IMPORT TAILWIND
# ============================================
# src/index.css
@import "tailwindcss";
# src/main.tsx
import './index.css'
# ============================================
# PART 4: RUN THE DEV SERVER
# ============================================
npm run dev
# Vite starts. Tailwind generates the CSS.
# The browser updates when classes change.
# ============================================
# PART 5: THE CLI SETUP
# ============================================
# In a project without Vite:
npm install tailwindcss @tailwindcss/cli
# ============================================
# PART 6: THE INPUT CSS
# ============================================
# src/input.css
@import "tailwindcss";
# ============================================
# PART 7: RUN THE CLI IN WATCH MODE
# ============================================
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch
# The CLI scans the project.
# The output.css file is generated.
# It is linked in the HTML.
# ============================================
# PART 8: THE PRODUCTION BUILD
# ============================================
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --minify
# The output.css is minified.
# ============================================
# PART 9: THE CSS-FIRST CONFIGURATION
# ============================================
# src/input.css
@import "tailwindcss";
@theme {
--color-brand: #16a34a;
--font-display: "Satoshi", sans-serif;
}
@utility content-auto {
content-visibility: auto;
}
# ============================================
# PART 10: THE PACKAGE.JSON SCRIPTS
# ============================================
{
"scripts": {
"dev": "vite",
"build": "vite build",
"tailwind:watch": "tailwindcss -i ./src/input.css -o ./src/output.css --watch",
"tailwind:build": "tailwindcss -i ./src/input.css -o ./src/output.css --minify"
}
}
The ten parts cover the Vite plugin setup, the Vite configuration, the Tailwind import, the dev server, the CLI setup, the input CSS, the watch mode, the production build, the CSS-first configuration, and the package scripts.
Quick Reference
The Vite Plugin Setup
| Step | Command |
|---|---|
| Install | npm install tailwindcss @tailwindcss/vite |
| Configure | Add tailwindcss() to vite.config.ts |
| Import | @import "tailwindcss"; in the CSS file |
| Run | npm run dev |
The CLI Setup
| Step | Command |
|---|---|
| Install | npm install tailwindcss @tailwindcss/cli |
| Watch | npx @tailwindcss/cli -i input.css -o output.css --watch |
| Build | npx @tailwindcss/cli -i input.css -o output.css --minify |
The Vite Plugin Options
| Option | Purpose |
|---|---|
optimize: true | Enable optimization and minification |
optimize: false | Disable optimization |
optimize: { minify: false } | Optimize without minifying |
The CLI Options
| Option | Alias | Purpose |
|---|---|---|
--input | -i | Input CSS file |
--output | -o | Output CSS file |
--watch | -w | Watch for changes |
--minify | -m | Minify the output |
--map | Generate source map |
The CSS-First Configuration
| Directive | Purpose |
|---|---|
@theme | Define design tokens |
@utility | Define custom utilities |
@variant | Define custom variants |
@source | Add a source directory to scan |
@config | Point to a legacy JS config |
Best Practices
✅ Do This:
# Use the Vite plugin for Vite projects
npm install tailwindcss @tailwindcss/vite # ✅
// Add the plugin to the Vite config
plugins: [react(), tailwindcss()] // ✅
/* Import Tailwind with a single statement */
@import "tailwindcss"; /* ✅ */
# Use the CLI for non-Vite projects
npx @tailwindcss/cli -i input.css -o output.css --watch # ✅
/* Configure with @theme in the CSS file */
@theme { --color-brand: #16a34a; } /* ✅ */
❌ Don’t Do This:
# Don't use the old tailwindcss CLI command
npx tailwindcss -i input.css -o output.css # ❌
/* Don't use the old @tailwind directives */
@tailwind base; @tailwind components; @tailwind utilities; /* ❌ */
# Don't create a tailwind.config.js for new projects
module.exports = { content: [...] } # ❌
# Don't install autoprefixer with the Vite plugin
npm install autoprefixer # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
@tailwind directives error | v3 syntax | Use @import "tailwindcss"; |
| CLI command not found | Wrong package name | Use @tailwindcss/cli |
| Styles not updating | Watch mode not running | Start --watch |
| Config file ignored | v4 uses CSS config | Use @theme in CSS |
| PostCSS conflict | Both plugin and PostCSS | Remove PostCSS config |
Real-World Examples
1. Vite Plugin Install
npm install tailwindcss @tailwindcss/vite
2. Vite Config
plugins: [react(), tailwindcss()]
3. Tailwind Import
@import "tailwindcss";
4. CLI Watch
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --watch
5. CLI Build
npx @tailwindcss/cli -i ./src/input.css -o ./src/output.css --minify
6. Theme Configuration
@theme { --color-brand: #16a34a; }
7. Custom Utility
@utility content-auto { content-visibility: auto; }
8. Source Directive
@source "../node_modules/@my-company/ui-lib";
9. Package Scripts
"dev": "tailwindcss -i ./src/input.css -o ./src/output.css --watch"
10. Vite Plugin Options
tailwindcss({ optimize: false })
Visual
The Two Integration Paths
┌──────────────────────────────────────────────┐
│ VITE PLUGIN │
│ npm install tailwindcss @tailwindcss/vite │
│ → add plugin to vite.config.ts │
│ → import "tailwindcss" in CSS │
│ → HMR and build handled by Vite │
│ │
│ CLI │
│ npm install tailwindcss @tailwindcss/cli │
│ → npx @tailwindcss/cli -i input -o output │
│ → watch mode for development │
│ → minify for production │
│ │
└──────────────────────────────────────────────┘
The Vite Plugin Architecture
┌──────────────────────────────────────────────┐
│ @tailwindcss/vite │
│ ├─ scan plugin (@tailwindcss/vite:scan) │
│ ├─ generate serve (@tailwindcss/vite:generate:serve)│
│ └─ generate build (@tailwindcss/vite:generate:build)│
│ │
│ The scan plugin initializes the config. │
│ The serve plugin generates CSS in dev. │
│ The build plugin optimizes for production. │
│ │
└──────────────────────────────────────────────┘
The CLI Workflow
┌──────────────────────────────────────────────┐
│ CLI WORKFLOW │
│ │
│ 1. Read input CSS │
│ 2. Scan source files for class names │
│ 3. Compile CSS │
│ 4. Optimize (optional) │
│ 5. Write output CSS │
│ │
│ Watch mode repeats on file changes. │
│ │
└──────────────────────────────────────────────┘
The CSS-First Configuration
┌──────────────────────────────────────────────┐
│ @import "tailwindcss"; │
│ │
│ @theme { │
│ --color-brand: #16a34a; │
│ --font-display: "Satoshi"; │
│ } │
│ │
│ @utility content-auto { │
│ content-visibility: auto; │
│ } │
│ │
│ Configuration lives in CSS. │
│ No tailwind.config.js needed. │
│ │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Vite plugin package | @tailwindcss/vite |
| CLI package | @tailwindcss/cli |
| Tailwind import | @import "tailwindcss"; |
| Vite config | Add tailwindcss() to plugins |
| CLI watch | npx @tailwindcss/cli -i input -o output --watch |
| CLI build | npx @tailwindcss/cli -i input -o output --minify |
| CSS-first config | @theme directive |
| Custom utilities | @utility directive |
| Custom variants | @variant directive |
| Source scanning | @source directive |
Key takeaways:
- Tailwind CSS v4 offers two integration paths: the Vite plugin and the standalone CLI. The Vite plugin integrates with the Vite build pipeline and provides HMR. The CLI is a standalone executable that works with any project structure .
- The Vite plugin is the recommended choice for Vite projects. It requires two packages:
tailwindcssand@tailwindcss/vite. The plugin is added tovite.config.ts, and the CSS file imports Tailwind with@import "tailwindcss";. - The CLI is the recommended choice for non-Vite projects. It is installed with
@tailwindcss/cli. The CLI reads an input CSS file, scans the project for class names, and writes an output CSS file. The--watchflag recompiles on changes . - Tailwind v4 uses CSS-first configuration. The
tailwind.config.jsfile is no longer the primary configuration method. The@themedirective defines design tokens. The@utilitydirective defines custom utilities. The@variantdirective defines custom variants . - The old
@tailwinddirectives are replaced by a single import. The v3 syntax@tailwind base; @tailwind components; @tailwind utilities;is gone. The v4 syntax is@import "tailwindcss";. - The old
npx tailwindcssCLI command does not work with v4. The CLI package is now@tailwindcss/cli. The command isnpx @tailwindcss/cli. - PostCSS configuration is not required when using the Vite plugin. The plugin handles the CSS processing internally. The
postcss.config.jsfile,autoprefixer, andpostcss-importare not needed .
Remember: Tailwind CSS v4 has two setup paths. Use the Vite plugin if your project uses Vite. Use the CLI if it does not. Both paths import Tailwind with a single @import "tailwindcss"; statement. Both paths configure the theme in CSS with @theme. Both paths produce the same output. The Vite plugin is faster in development. The CLI is simpler in projects without Vite. Choose the path that matches your build tool.
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!