AI Trends9 min

Upgraded to Tailwind v4 — Config Files Are Gone

Migrated to Tailwind CSS v4. tailwind.config.js is gone, and configuration is done in a single CSS file. The Rust-based Oxide compiler makes builds 5x faster. Documented the full process of moving from v3 to v4.

On this page (10)

April 2026 · GoCodeLab

Tailwind CSS v4 Migration Practical Guide

Tailwind CSS v4 is officially out. tailwind.config.js is gone. Configuration now lives directly inside the CSS file. I applied it to a Next.js project myself. The conclusion up front — it feels unfamiliar, but it's simpler.

If you're coming from a v3 project, everything changes — config files, packages, all of it. PostCSS config changed too. It's disorienting at first, but the actual transition is faster than expected. One automatic CLI run handles 80% of it.

This post covers everything from installation to @theme syntax, renamed classes, and custom plugin handling. If you're considering a v4 migration, read from the top. If it's a new project, jump straight to the "When to upgrade" section.

Quick summary
  • tailwind.config.js → replaced by a CSS @theme block
  • Rust-based Oxide compiler — up to 5x faster full builds, up to 100x faster incremental builds
  • Automatic content detection — no need to configure a content array
  • Define custom tokens directly as CSS variables (no JS required)
  • Automatic migration available via npx @tailwindcss/upgrade
  1. Config moved into CSS
  2. Deep dive: @theme syntax
  3. The Oxide compiler changed everything
  4. Steps to migrate from v3 to v4
  5. Next.js + PostCSS setup
  6. Renamed classes checklist
  7. Handling custom plugins
  8. v3 vs v4 at a glance
  9. When to upgrade
  10. FAQ

Config moved into CSS

Up through v3, everything was configured in a JS file. v4 does it all in a single CSS file. Start with one line — @import "tailwindcss" — then put custom values inside an @theme block. One less JS dependency.

If you were using the three-line setup — @tailwind base;, @tailwind components;, @tailwind utilities; — delete all of them. They're replaced by a single @import "tailwindcss". The directives got simpler.

@theme works as CSS variables. It's a structure for managing design tokens inside CSS without any JS. Values are immediately visible in browser DevTools. That means config values are exposed outside the build output.

/* v4: configure directly in the CSS file */
@import "tailwindcss";

@theme {
  --font-display: "Pretendard Variable", sans-serif;
  --color-brand: oklch(0.70 0.18 250);
  --color-brand-dark: oklch(0.55 0.20 250);
  --spacing-gutter: 2rem;
  --breakpoint-3xl: 1920px;

  /* Reset entire namespace then redefine */
  --color-*: initial;
  --color-gray-50: #f9fafb;
}

Deep dive: @theme syntax

Variables inside the @theme block follow a naming convention: --color-{name}, --font-{name}, --spacing-{name}. Tailwind reads these namespaces and automatically generates utility classes. Define --color-brand and text-brand, bg-brand classes are immediately available.

The oklch() format is recommended for color definitions. oklch is the successor to HSL — a color space that renders consistent perceived lightness on screen. Define colors like oklch(0.70 0.18 250) using lightness, chroma, and hue angle. Fallbacks for older browsers are handled automatically.

The --color-*: initial syntax resets an entire namespace. Use it when you want to wipe the default color palette and keep only your design system tokens. It prevents unnecessary utilities from being included in the build.

@theme variable namespace reference
--color-* → text-*, bg-*, border-* and other color utilities
--font-* → font-{name} font families
--text-* → text-{size} font sizes
--spacing-* → p-*, m-*, gap-* and other spacing utilities
--breakpoint-* → sm:, md:, lg: and other responsive breakpoints
--radius-* → rounded-{name} border radii

The Oxide compiler changed everything

The reason v4 is faster is the Oxide compiler, written in Rust. Oxide replaces the existing Node.js pipeline with Rust. Official benchmarks show up to 5x faster full builds and up to 100x faster incremental builds.

Content path detection is also automatic now. In v3, you had to manually write things like content: ['./src/**/*.tsx']. v4 scans the project structure and finds everything on its own. Fewer things to configure.

Oxide ships inside the tailwindcss v4 package — no separate installation needed. It activates automatically on install. It connects to existing pipelines through Vite or PostCSS plugins. The migration doesn't significantly change your workflow.

Steps to migrate from v3 to v4

There's an official upgrade CLI. Running npx @tailwindcss/upgrade automatically handles config file conversion and class renames. For projects without custom plugins, that one line is all it takes.

If you're using PostCSS, replace it with @tailwindcss/postcss. For Vite projects, use the @tailwindcss/vite plugin. Next.js goes through the @tailwindcss/postcss path.

# Step 1: Run the automatic migration CLI
npx @tailwindcss/upgrade@next

# Step 2: Replace packages (PostCSS path)
npm install tailwindcss@next @tailwindcss/postcss

# For Vite projects
npm install tailwindcss@next @tailwindcss/vite

After running the automatic CLI, always review the diff. Dynamically composed class names (patterns like bg-${color}-500) are not automatic migration targets. Projects using CSS-in-JS libraries alongside Tailwind also need manual verification.

Next.js + PostCSS setup

The manual setup for Next.js is three steps: replace the package, update the PostCSS config, and update the CSS file. The automatic CLI handles all three at once, but doing it manually helps you understand the structure.

// postcss.config.js (v4)
export default {
  plugins: {
    '@tailwindcss/postcss': {}
  }
};

/* globals.css (v4) */
/* Replace the three @tailwind directives with one line */
@import "tailwindcss";

@theme {
  --font-sans: "Pretendard Variable", sans-serif;
}

tailwind.config.js can be deleted or left as-is. v4 doesn't read it. If you're working in a team, deleting it is cleaner to avoid confusion.

Next.js v4 migration checklist
  • Run npm install tailwindcss@next @tailwindcss/postcss
  • In postcss.config.js, replace tailwindcss with @tailwindcss/postcss
  • In globals.css, replace the three @tailwind lines with @import "tailwindcss"
  • Move custom values from tailwind.config.js into the @theme block
  • After building, check for broken styles (especially dynamic class names)

Renamed classes checklist

Some class names changed. The automatic CLI handles most of them, but CSS-in-JS and dynamic class name composition patterns need manual review. The list below focuses on the ones that come up most often in practice.

v3 v4 Notes
@tailwind base/components/utilities @import "tailwindcss" One line at the top of the CSS file
outline-none outline-hidden When the intent is hiding the focus ring
plugins: [require('...')] @plugin "..." Declared in CSS
theme.extend.colors.brand: '...' --color-brand: ... Inside the @theme block
content: ['./src/**/*.tsx'] Not needed Auto-detected
theme() CSS function var(--color-brand) Direct CSS variable reference

outline-none deserves special attention. In v3 it rendered as outline: 2px solid transparent. In v4 it changed to outline: none. Code that used it to hide focus rings on buttons or inputs must be replaced with outline-hidden. This can surface during accessibility testing.

Handling custom plugins

Existing plugins are now declared in CSS using the @plugin directive. The plugins array in tailwind.config.js is gone, so the approach changed. For official plugins, just pass the package name.

@import "tailwindcss";

/* Official plugins */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";

/* Custom plugins */
@plugin "./plugins/my-plugin.js";

@theme {
  --color-brand: oklch(0.70 0.18 250);
}

For custom JS plugins, pass the file path to @plugin and it works. Existing APIs like addUtilities and addComponents are mostly preserved. However, parts of the v4 plugin API changed, so verify behavior after migrating. Check the official plugin migration guide for details.

v3 vs v4 at a glance

Feature v3 v4
Configuration tailwind.config.js CSS @theme block
Compiler Node.js Rust (Oxide)
Content detection Manual path config Automatic
Custom tokens theme.extend (JS) CSS variables (@theme)
Build speed Baseline Up to 5x faster
Incremental build Baseline Up to 100x faster
Plugin declaration plugins array (JS) @plugin directive (CSS)
Auto migration None @tailwindcss/upgrade CLI
Situation Recommendation Reason
New project Start with v4 Simpler and faster
Build speed is a bottleneck Upgrade to v4 Up to 100x faster incremental builds
Complex custom JS plugins Stay on v3 and assess API changes require manual verification
Vite-based project Good fit for v4 Official Vite plugin supported
Mixed CSS-in-JS usage Manual testing before switching Dynamic class names not auto-detected

When to upgrade

For a new project, v4 is the right starting point. It's simpler and faster than v3. The biggest long-term advantage is managing design tokens in CSS alone, without a JS config file. Even designers on the team who don't know JS can edit CSS variables directly.

For existing projects, it depends. If build speed is noticeably slow, or tailwind.config.js keeps growing in complexity, there's a solid case for upgrading now. On the other hand, if you have many complex custom plugins or are deeply tied to a legacy PostCSS pipeline, taking it slow is fine.

v3 continues to be supported. There's no urgency to move to v4. That said, all new features and performance improvements will only land in v4. It doesn't have to be right now, but planning the migration within the year is realistic.

Signs you should hold off on v4
  • You have 5 or more custom JS plugins with complex internal logic
  • You're using CSS-in-JS (styled-components, Emotion) alongside Tailwind
  • Legacy @apply patterns are spread throughout the project
  • The team doesn't have bandwidth for migration work this quarter

FAQ

Q. Do I need to completely delete tailwind.config.js?

v4 doesn't read it. It's ignored even if it's there. Deleting it is cleaner to avoid team confusion. If you use the automatic CLI, it converts the contents to @theme and handles the file for you.

Q. Does the Oxide compiler need to be installed separately?

It's included in the tailwindcss@next package — no separate installation needed. It activates automatically on install. Whether you connect it through Vite or PostCSS, Oxide runs by default.

Q. How do I use plugins like @tailwindcss/typography in v4?

Declare them in the CSS file like @plugin "@tailwindcss/typography". The plugins array in tailwind.config.js is gone. When using multiple plugins, list each @plugin line in order.

Q. How long does it take to upgrade an existing Next.js project to v4?

For projects without custom plugins, it's done in under 30 minutes including the automatic CLI. Complex plugins add manual verification on top. If you're mixing CSS-in-JS, you'll need to go through all dynamic class name patterns manually.

Q. Do I need to upgrade my v3 project to v4 right now?

There's no rush. v3 continues to be supported. v4 makes sense for new projects or when build speed is a bottleneck. If you have many complex custom plugins, staying on v3 is more stable. Planning the migration on a quarterly basis is realistic.

v4 completely changes the config structure. If you're used to JS config files, it feels unfamiliar at first. For new projects, starting on v4 is the right call. For existing projects, run the automatic CLI first and handle the custom plugin parts manually.

The build speed difference is real. On large projects, the 100x incremental build improvement is noticeable. Managing config in a single CSS file means team members who don't know JS can still edit design tokens. That's the biggest long-term win.

This post was written based on the official Tailwind CSS v4 release. Some details may change with future version updates.

Last updated: April 2026

Share