# Tailwind Impl Build Vite

> Use when wiring Tailwind CSS into a Vite project: choosing between the v4 dedicated Vite plugin and the v3 PostCSS pipeline, picking the right packages and configs, importing the stylesheet, enabling HMR, and scoping template-scan paths in monorepos. Prevents the wrong-plugin trap (using postcss-plugin with v4 instead of @tailwindcss/vite gives much slower builds and broken at-rules), the broken-classnames trap (writing @import "tailwindcss" in v3 yields no utilities), the missing-content trap (v3 without content paths produces an empty stylesheet), the missing-source trap (v4 in a monorepo where workspace templates live outside the Vite root never get scanned), the upgrade trap (v4-0-8 Astro break, issue 16733), and the wrong-tool trap (chasing Turbopack issue 19825 in a Vite project). Covers v4 with @tailwindcss/vite (install, vite-config-ts plugin entry, @import "tailwindcss" in the entry CSS, automatic content detection, @source for monorepo/external paths, HMR), v3 with postcss-plugin (npm install -D tai

- Skill: `impertio-studio/tailwind-impl-build-vite` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/tailwind-impl-build-vite`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/tailwind-impl-build-vite/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/tailwind-impl-build-vite

---


# Tailwind CSS Build Integration with Vite

Vite has two completely separate Tailwind integration paths:

- **v4** : the dedicated `@tailwindcss/vite` plugin (preferred for v4)
- **v3** : the legacy `tailwindcss` PostCSS plugin (used via Vite's built-in PostCSS)

ALWAYS match plugin to version. NEVER use `@tailwindcss/postcss` with v4 inside
Vite when `@tailwindcss/vite` is available : the dedicated plugin is faster,
supports the v4 at-rules natively, and integrates with Vite's HMR and
dependency graph.

Companion skills :

- `tailwind-impl-config-v3` : the v3 JS-config surface
- `tailwind-impl-config-v4` : the v4 CSS-first config surface
- `tailwind-core-v3-vs-v4` : behavioral differences between versions

## Quick Reference

### v4 with @tailwindcss/vite (preferred)

Install :

```bash
npm install tailwindcss @tailwindcss/vite
```

`vite.config.ts` :

```ts
import { defineConfig } from "vite"
import tailwindcss from "@tailwindcss/vite"

export default defineConfig({
  plugins: [
    tailwindcss(),
  ],
})
```

`src/style.css` (entry stylesheet) :

```css
@import "tailwindcss";
```

Reference the entry stylesheet from your HTML or framework entry
(`<link rel="stylesheet" href="/src/style.css">` for vanilla, `import
"./style.css"` from `main.ts` for React/Vue/Svelte/SolidJS).

No `tailwind.config.js` is required. v4 auto-detects template files
relative to the Vite root via `.gitignore` and source-scanner heuristics.

### v3 with PostCSS

Install :

```bash
npm install -D tailwindcss@3 postcss autoprefixer
npx tailwindcss init -p
```

The `-p` flag also generates `postcss.config.js`. Vite reads it automatically.

`tailwind.config.js` :

```js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx,vue,svelte}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

`postcss.config.js` (generated by `init -p`) :

```js
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
```

`src/index.css` :

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

ALWAYS list every template extension you actually use in `content`.
Anything not matched is invisible to the JIT scanner and produces no CSS.

## Decision Trees

### Which plugin should I use ?

```
Tailwind major version ?
├── v4 (4.0.0 and above)
│   └── ALWAYS @tailwindcss/vite
│       NEVER @tailwindcss/postcss in a Vite project
│       NEVER the v3 tailwindcss postcss-plugin (it will not parse v4 at-rules)
└── v3 (3.x)
    └── ALWAYS the tailwindcss postcss-plugin via postcss.config.js
        NEVER @tailwindcss/vite (it only exists for v4)
```

### What entry CSS should I write ?

```
Version ?
├── v4 : @import "tailwindcss";
│        (single line, no @tailwind directives)
└── v3 : @tailwind base;
         @tailwind components;
         @tailwind utilities;
         (all three required, in this order)
```

### My classes are not applying. Where do I look ?

```
1. Is the entry CSS imported into the app entry ?
   ├── No  : add import "./style.css" in main.ts (or <link> in index.html)
   └── Yes : continue
2. Is the Tailwind plugin registered in vite.config.ts (v4) or
   postcss.config.js (v3) ?
   ├── No  : add it
   └── Yes : continue
3. v3 : does tailwind.config.js content include the files you write classes in ?
   ├── No  : add the glob (e.g. "./src/**/*.{ts,tsx}")
   └── Yes : continue
4. v4 : are the files inside the Vite root, and not gitignored ?
   ├── Outside root or gitignored : add @source "path"; in your entry CSS
   └── Yes : continue
5. Are class names produced by string concatenation
   (e.g. "text-" + color) ?
   └── Yes : the JIT scanner cannot see them, use safelist
       (v3 safelist in config, v4 @source inline "{...}" brace expansion)
6. Inspect the built CSS in DevTools : if utilities are missing,
   the scanner did not see your file. If present but not applied,
   it is a specificity or order issue, not a Tailwind setup issue.
```

### Monorepo : how do I include workspace packages ?

```
Version ?
├── v3 : extend content paths in tailwind.config.js :
│        content: [
│          "./index.html",
│          "./src/**/*.{ts,tsx,vue,svelte}",
│          "../../packages/ui/src/**/*.{ts,tsx}",
│        ],
└── v4 : add @source entries to the entry CSS :
         @import "tailwindcss";
         @source "../../packages/ui/src/**/*.{ts,tsx}";
         (the v4 plugin auto-detects files only within the Vite root
          and respects .gitignore, so external packages need @source)
```

## Patterns

### Pattern : framework-flavored Vite setup (v4)

For React (`@vitejs/plugin-react`), Vue (`@vitejs/plugin-vue`), Svelte
(`@sveltejs/vite-plugin-svelte`), SolidJS (`vite-plugin-solid`), or Qwik :
add `tailwindcss()` to the `plugins` array. Order does not matter for
Tailwind's CSS pipeline, but ALWAYS keep the framework plugin first
when a framework provides its own CSS preprocessor (Svelte, Vue) so
component-scoped styles are parsed before Tailwind sees them.

```ts
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import tailwindcss from "@tailwindcss/vite"

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),
  ],
})
```

### Pattern : single-file CSS for v3 vs v4

v4 entry CSS :

```css
@import "tailwindcss";

@theme {
  --color-brand: oklch(0.7 0.2 145);
}
```

v3 entry CSS :

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --brand: 220 90% 56%;
  }
}
```

NEVER mix the two : the three v3 directives are silently ignored by v4,
and `@import "tailwindcss"` is meaningless in v3.

### Pattern : HMR for design tokens

HMR works out of the box in both versions. Editing the entry CSS,
`tailwind.config.js` (v3), or any file matched by content/`@source`
triggers a stylesheet swap with no full reload. ALWAYS rely on this :
NEVER write a custom Vite plugin to watch Tailwind files.

If HMR appears broken, the cause is almost always one of :

1. The file you edited is not in `content` (v3) or not in the Vite root
   without an `@source` entry (v4).
2. A separate dev server proxy strips Vite's WebSocket connection.
3. The browser cached the old stylesheet : hard-reload once.

### Pattern : library-mode and SSR

In `build.lib` mode, the Tailwind plugin still runs and emits the
generated stylesheet alongside your library bundle. ALWAYS export the
stylesheet path so consumers can import it. For SSR (`vite-node`, SvelteKit,
Astro, Nuxt), the plugin runs once per build : no extra setup is needed
beyond standard SSR-safe usage of the generated CSS.

## Anti-Patterns

NEVER install both `@tailwindcss/vite` and `@tailwindcss/postcss` in
the same Vite project. They both transform CSS and double-process at-rules.

NEVER add `@tailwind base/components/utilities` to a v4 entry stylesheet.
v4 ignores those directives. The single `@import "tailwindcss"` replaces all three.

NEVER write `@import "tailwindcss"` in a v3 entry. v3 has no such import
target and will leave the line unresolved, producing zero utilities.

NEVER configure `content` in `tailwind.config.js` when using v4 with
`@tailwindcss/vite`. The v4 scanner ignores the JS config unless you
opt in via `@config`. Use `@source` directives in CSS instead.

NEVER set `optimizeDeps.exclude: ["tailwindcss"]` in `vite.config.ts`.
It is a runtime non-issue and only slows the first dev start.

See `references/anti-patterns.md` for the full catalog with quoted
error symptoms and fixes.

## Common Breakage

### v4.0.8 Astro regression (tailwindlabs/tailwindcss#16733)

Tailwind v4.0.8 + Astro + `@tailwindcss/vite` regressed parsing of
component-package styles. Symptom : utilities that worked under 4.0.7
silently stopped applying after upgrade.

ALWAYS pin to 4.0.7 OR upgrade past the patched release if you are on
Astro + Vite and hit missing utilities right after a 4.0.8 bump :

```json
"dependencies": {
  "tailwindcss": "4.0.7",
  "@tailwindcss/vite": "4.0.7"
}
```

### Turbopack arbitrary-value miss (tailwindlabs/tailwindcss#19825)

Issue 19825 reports arbitrary-value classes like `aspect-[12/5]`,
`z-[100]`, `h-[80vh]` missing in Next.js + Turbopack builds.

This bug is Turbopack-specific. Vite users are NOT affected.
If you are on Vite and arbitrary-value classes go missing, the cause
is almost always a missing `@source` (v4) or content path (v3),
NEVER this Turbopack bug.

## Verification

After install, ALWAYS verify with a single class that exercises the JIT :

```html
<div class="text-3xl font-bold underline">Tailwind is wired</div>
```

If the text becomes large, bold, and underlined in the dev server,
the entire chain (plugin registered, entry CSS imported, scanner
seeing your template) is working.

For arbitrary values, also try :

```html
<div class="text-[#ff00aa] h-[42vh]">Arbitrary values</div>
```

If arbitrary values fail but utility classes work, the JIT engine
is running but content/source scope is wrong.

## Reference Links

- `references/methods.md` : exhaustive API of @tailwindcss/vite plugin
  options, v3 PostCSS options, init flags, package commands
- `references/examples.md` : full project examples for React, Vue, Svelte,
  SolidJS, Astro, Qwik, both v3 and v4, plus monorepo and library-mode
- `references/anti-patterns.md` : every known wiring mistake with the
  exact error or symptom, the cause, and the fix

## Sources

- v4 install for Vite : https://tailwindcss.com/docs/installation/using-vite
- v3 install for Vite : https://v3.tailwindcss.com/docs/guides/vite
- v4 directives reference : https://tailwindcss.com/docs/functions-and-directives
- @tailwindcss/vite plugin : https://github.com/tailwindlabs/tailwindcss/tree/next/packages/%40tailwindcss-vite
- v4.0.8 Astro regression : https://github.com/tailwindlabs/tailwindcss/issues/16733
- Turbopack arbitrary-value miss (NOT Vite) : https://github.com/tailwindlabs/tailwindcss/issues/19825

