Migrating to Tailwind CSS v4
Tailwind v4 drops the JavaScript config file in favor of CSS-based configuration. The migration is mostly mechanical but there are gotchas with the @apply directive and custom variants.
CSS-First Configuration
The biggest change: tailwind.config.js is replaced by CSS. Your design tokens live in the @theme directive:
@import "tailwindcss";
@theme {
--color-primary: #2563eb;
--color-primary-dark: #1d4ed8;
--font-sans: "Inter", sans-serif;
--spacing-container: 1280px;
}VSCode IntelliSense still works — the Tailwind CSS extension reads theme values from your stylesheet.
The Migration Path
- Install v4:
npm install tailwindcss@next - Run the automatic migration tool:
npx @tailwindcss/upgrade - Manually check
@applyusages — the tool handles most cases, but deeply nested@applywith custom utilities sometimes breaks. - Replace
theme.extendin your old config with@themeblocks in CSS.
New Features Worth Using
Container Queries
No more media queries for component-level responsiveness. Tailwind v4 ships container query utilities:
<div class="@container">
<div class="grid @lg:grid-cols-2">...</div>
</div>Dynamic Utility Values
Arbitrary values are simpler: w-(--sidebar-width) references a CSS custom property. No more bracket syntax for common cases.
Native Cascade Layers
Tailwind v4 uses @layer to control specificity. Base, components, and utilities are layered in the correct order — @apply in component layer no longer risks specificity wars.
Gotchas
- Custom variants defined in old JS config need to be re-registered via
@variantin CSS. - The
importantconfig option now needs an explicit CSS strategy. - Some plugins haven’t updated yet — check compatibility before migrating production projects.
Overall, the migration took about two hours for a mid-sized project. The CSS-first approach feels more natural, and the build is noticeably faster thanks to the new Oxide engine.