Software Engineering

Tailwind v4 Not Working with Vite? The 3-Line Fix (and Why v3 Guides Break)

Tailwind CSS not working in Vite? v4 changed the setup: the @tailwindcss/vite plugin, one @import line, no init command. Tested fix, migration table, pitfalls.

If Tailwind isn’t working in your Vite project, you are almost certainly following a guide written for Tailwind v3 while npm install tailwindcss gives you v4. Version 4 changed the setup: install the @tailwindcss/vite plugin, add it to vite.config.ts, and replace the three @tailwind lines in your CSS with @import "tailwindcss";. There is no init command and no config file to create.

Everything here was tested on 26 October 2026 with Tailwind CSS 4.3.3 (and 3.4.19 for the v3 parts), Vite 8.3.5 from create-vite 9.3.1, React 19.3, Vue 3.5, Next.js 16.4.3 and Node.js 22.14.

Which symptom do you have?

Tailwind 4.0 came out in January 2025, so most tutorials and courses written before then fail in one of three ways:

What you see Why
npx tailwindcss init -p prints npm error could not determine executable to run The v4 tailwindcss package has no command at all; the CLI moved to @tailwindcss/cli, which has no init either
The build fails: It looks like you're trying to use tailwindcss directly as a PostCSS plugin A v3 postcss.config.js that lists tailwindcss as a plugin
The build succeeds, but the page has no Tailwind styles The v3 @tailwind base; @tailwind components; @tailwind utilities; lines in your CSS

The third one is the confusing one. With the v4 Vite plugin installed and the old @tailwind lines left in place, the build finishes without a single warning — and generates no utility classes at all. In testing, text-3xl and bg-sky-500 simply weren’t in the output CSS.

The fix: Vite with React, Vue, Svelte or plain JS

1. Install Tailwind and its Vite plugin:

Shell
npm install tailwindcss @tailwindcss/vite

2. Add the plugin to vite.config.ts, next to your framework’s plugin (vue(), svelte(), or none for plain JS):

TypeScript
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'

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

3. Replace everything Tailwind-related in your main CSS file with one line:

CSS
/* src/index.css */
@import "tailwindcss";

That CSS file must be imported by your entry file — import './index.css' in src/main.tsx (or ./style.css in src/main.ts for the Vue template), which create-vite already does. If you had a postcss.config.js from a v3 guide, delete it, and remove tailwind.config.js unless you want to keep using it (see below). Then restart npm run dev.

What changed from v3 to v4

v3 habit v4 equivalent
npx tailwindcss init -p Nothing to run: no config file is needed
@tailwind base; @tailwind components; @tailwind utilities; @import "tailwindcss";
theme.extend in tailwind.config.js An @theme { … } block in your CSS
content: ["./src/**/*.{js,ts,jsx,tsx}"] Automatic detection; add other folders with @source
postcss.config.js with tailwindcss and autoprefixer Vite: @tailwindcss/vite. Other PostCSS setups: @tailwindcss/postcss. No autoprefixer: prefixes are added for you (the build output contained -webkit-backdrop-filter without it)
npx tailwindcss -i in.css -o out.css npx @tailwindcss/cli -i in.css -o out.css

Some class names now mean something else

If you migrate by hand, watch for the utilities v4 renamed. Nothing errors, so the differences are easy to miss: the bare shadow, rounded and blur still work as before, but every -sm version now means what the bare one used to, so shadow-sm, rounded-sm and blur-sm look a step larger unless you change them to -xs:

v3 v4
shadow-sm shadow-xs
shadow shadow-sm
rounded-sm rounded-xs
rounded rounded-sm
blur-sm, backdrop-blur-sm blur-xs, backdrop-blur-xs
outline-none outline-hidden
ring ring-3

outline-none now really sets outline-style: none; the old behaviour, an invisible outline that still shows in forced-colours mode, is outline-hidden. Two defaults changed too: borders now use currentColor instead of gray-200, and ring is 1px instead of 3px. The upgrade tool handles all of this for you.

Customising the theme

Theme values are now CSS variables in an @theme block:

CSS
@import "tailwindcss";

@theme {
  --color-brand: oklch(0.62 0.19 255);
  --font-display: "Inter", sans-serif;
}

That gives you bg-brand, text-brand, font-display and the rest. The build turns the colour into a real variable, --color-brand, which you can also use in your own CSS.

Already have a tailwind.config.js that you don’t want to rewrite yet? Point the stylesheet at it; the path is relative to the CSS file:

CSS
@import "tailwindcss";
@config "../tailwind.config.js";

In testing, a v3-style config with theme.extend.colors.brand produced bg-brand this way.

Other reasons Tailwind classes don’t appear

The class is in a file Tailwind doesn’t scan

Tailwind v4 finds class names on its own, starting from the current working directory. It skips files listed in .gitignore, everything in node_modules, and anything outside that starting folder. In testing, all three cases produced no CSS, even though the app imported every one of those files:

  • a component in a gitignored folder (generated code, for example),
  • a UI library inside node_modules,
  • a shared package in a sibling folder of a monorepo, such as ../shared-ui.

Register each one with @source, relative to the CSS file:

CSS
@import "tailwindcss";
@source "./generated";             /* a gitignored folder */
@source "../node_modules/fake-ui"; /* a package in node_modules */
@source "../../shared-ui/src";     /* a sibling package in a monorepo */

Use your own folder names. With those three lines, all the missing classes appeared. Because detection starts from the working directory, running the build from a different folder — a monorepo root, say — can also change what gets scanned.

@apply fails in a Vue <style> block or a CSS module

Vue and Svelte <style> blocks and CSS modules (*.module.css) are processed separately from your main stylesheet, so they don’t know about Tailwind’s classes. The build stops with:

Cannot apply unknown utility class `text-2xl`. Are you using CSS modules or similar and missing `@reference`?

Add @reference with the path to your main stylesheet. It makes the classes available to @apply without copying any of Tailwind’s CSS into the component:

HTML
<style scoped>
@reference "./style.css";

h1 {
  @apply text-2xl font-bold;
}
</style>

The same line fixes a CSS module: @reference "./index.css"; at the top of Title.module.css. Both were tested; the Svelte case works the same way, according to Tailwind’s documentation.

The class name is built at runtime

Tailwind reads your source files as text; it never runs them. A class that only exists after string concatenation is never generated — this was already true in v3:

TypeScript
// ✗ Tailwind can't see this
<p className={`text-${color}-500`} />

// ✓ Use complete class names
const colors = { red: "text-red-500", blue: "text-blue-500" };
<p className={colors[color]} />

In testing, text-red-500 and text-blue-500 from the map were generated; text-green-500 and text-purple-500 from the template string were not.

The browser is too old

Tailwind v4 is designed for Safari 16.4+, Chrome 111+ and Firefox 128+. If you must support older browsers, Tailwind’s own advice is to stay on v3.4.

Autocomplete stopped working in the editor

The Tailwind CSS IntelliSense extension finds a v4 project through a CSS file that contains @import "tailwindcss", not through tailwind.config.js. Update the extension; if it still can’t find your stylesheet, point it there with the tailwindCSS.experimental.configFile setting, for example "tailwindCSS.experimental.configFile": "src/index.css".

Using Next.js instead of Vite?

Next.js uses PostCSS, so you install the PostCSS plugin instead of the Vite one:

Shell
npm install tailwindcss @tailwindcss/postcss
JavaScript
// postcss.config.mjs
export default { plugins: { "@tailwindcss/postcss": {} } };
CSS
/* app/globals.css */
@import "tailwindcss";

Import ./globals.css in app/layout.tsx. Tested with Next.js 16.4.3: the built CSS contained the utilities used in the layout.

Staying on v3

For an existing project you aren’t ready to migrate, or for older browsers, install v3 explicitly — then the old commands work again:

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

This installed 3.4.19 and created tailwind.config.js and postcss.config.js. Remember the step v3 guides include and v4 doesn’t need: list your files in content, for example ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], or no classes are generated. For a new project, use v4: the setup is shorter, and it’s where Tailwind’s development happens.

Upgrading an existing v3 project

Tailwind ships an upgrade tool. It needs Node.js 20 or later; run it on a clean Git branch so you can review every change:

Shell
npx @tailwindcss/upgrade

On a v3 test project, it:

  • moved theme.extend from tailwind.config.js into an @theme block, and deleted the config file;
  • replaced the @tailwind lines with @import 'tailwindcss';;
  • renamed classes in the components and in @apply (backdrop-blur-sm to backdrop-blur-xs, shadow to shadow-sm);
  • added a small @layer base rule that keeps v3’s grey default border colour;
  • switched postcss.config.js to @tailwindcss/postcss, and removed autoprefixer.

The project built afterwards with the same classes in the output. The tool kept the PostCSS setup rather than switching to the Vite plugin; both work.

Checklist

  1. Check the installed version: npm ls tailwindcss.
  2. v4 with Vite: @tailwindcss/vite in vite.config.ts, and @import "tailwindcss"; in a CSS file your entry imports.
  3. Delete the @tailwind lines and any v3 postcss.config.js; don’t run init.
  4. Put theme changes in @theme, or point to an old config with @config.
  5. Add @source for gitignored folders, packages in node_modules and sibling packages.
  6. Add @reference before @apply in Vue or Svelte <style> blocks and CSS modules.
  7. Write complete class names; never build them from strings.
  8. Migrating by hand? Check the renamed utilities (shadow, rounded, blur, ring, outline-none).
  9. Prefer npx @tailwindcss/upgrade on a clean branch, then review the diff.
  10. Need older browsers? Stay on tailwindcss@3.

References and further reading

  1. Installing Tailwind CSS with Vite, Tailwind CSS documentation
  2. Upgrade guide, Tailwind CSS documentation
  3. Detecting classes in source files, Tailwind CSS documentation
  4. Functions and directives, Tailwind CSS documentation
  5. Tailwind CSS IntelliSense, Tailwind Labs, on GitHub

Written by

Thanush BD

Software engineer in Bengaluru working where software meets environmental engineering. At Ecologic Engineering I build audit, compliance, monitoring and water-treatment software — mostly backend: data models, APIs, pipelines and scheduled jobs.