Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Tailwind CSS 4 What Changed and How to Migrate

By Kokil Thapa | Last reviewed: September 2026

Tailwind CSS 4 What Changed and How to Migrate is the question every team asks after a major release reshapes the build pipeline. Version 4 replaces the PostCSS-only workflow with an Oxide engine, moves configuration into CSS, and drops the old @tailwind directives. If you ship production web applications in Nepal or abroad, the upgrade affects every Blade template, Vite config, and CI build step you maintain.

What are the biggest changes in Tailwind CSS 4?

Tailwind CSS 4 is a ground-up rewrite, not a patch release. The team rebuilt the compiler in Rust under the Oxide project. Builds on large codebases often finish in milliseconds instead of seconds. That speed matters when your GitLab CI pipeline runs lint, test, and asset build on every push.

The configuration model flipped entirely. Tailwind CSS 3 expected a JavaScript tailwind.config.js file. Version 4 prefers CSS-native directives. You define design tokens inside @theme blocks in your main stylesheet. The old config file still works through a compatibility layer, but new projects should start CSS-first.

Import syntax changed too. The three @tailwind base, @tailwind components, and @tailwind utilities directives are gone. One line replaces them:

@import "tailwindcss";

Content detection is smarter but less magical than it sounds. Tailwind scans files automatically when you use the Vite plugin. For Laravel Blade views, pagination templates, and compiled views in storage/framework/views, you still declare paths explicitly with @source. I have missed compiled Blade paths on client projects and wondered why pagination styles vanished after deploy.

Tailwind CSS 4 Core ArchitectureSource FilesBlade, Vue, JSOxide EngineRust compilerCSS OutputUtility classesCSS-First Config@import, @theme, @source in app.css@tailwindcss/viteRecommended bundler pluginLegacy Path@tailwindcss/postcss
Tailwind CSS 4 What Changed — Oxide engine compiles utilities from CSS-native configuration through Vite or PostCSS

Native CSS features got first-class support. Custom properties defined in @theme become real CSS variables at runtime. You can reference them in plain CSS or inline styles without a build step. Container queries, @starting-style, and modern color formats like OKLCH work out of the box.

The npm package structure split. Core lives in tailwindcss. Bundler integration ships as @tailwindcss/vite or @tailwindcss/postcss. The official upgrade CLI is @tailwindcss/upgrade. Keep these names straight when reading older tutorials that still reference a single PostCSS plugin chain.

Version 3 vs version 4 at a glance

AreaTailwind CSS 3Tailwind CSS 4
Entry directive@tailwind base/components/utilities@import "tailwindcss"
Configurationtailwind.config.js@theme in CSS (JS optional)
CompilerJavaScript (PostCSS)Oxide (Rust)
Content pathscontent: [] in config@source directives in CSS
Vite setupPostCSS plugin onlyDedicated @tailwindcss/vite plugin
Default ring width3px1px
Default border colorgray-200currentColor

For a broader picture of framework upgrades in 2026, see the Laravel 12 features and changes guide. Laravel and Tailwind often upgrade together on the same sprint.

How do you upgrade a Tailwind CSS 3 project to version 4?

Start with a clean git branch and a passing test suite. Tailwind CSS 4 What Changed and How to Migrate is safest when you can diff visual regressions quickly. Storybook helps. So does a staging environment that mirrors production assets.

Run the official upgrade tool first. It rewrites config files, updates imports, and flags deprecated utilities:

npx @tailwindcss/upgrade

The CLI targets Node.js 20 or newer. Your CI runners must match. On projects I maintain with Alpine.js in Blade templates, the upgrade tool preserves most class names unchanged. Custom plugins need manual review.

Install the new packages and remove obsolete ones:

npm uninstall tailwindcss postcss autoprefixer
npm install tailwindcss @tailwindcss/vite

Update your Vite config. With Vite 8.x on a Laravel 13 project, the plugin replaces the PostCSS chain entirely:

import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/css/app.css', 'resources/js/app.js'],
      refresh: true,
    }),
    tailwindcss(),
  ],
})

Replace your CSS entry file contents:

@import "tailwindcss";

@source '..//*.blade.php';
@source '..//*.js';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';

Delete postcss.config.js unless you rely on other PostCSS plugins. Autoprefixer is built into Tailwind CSS 4. Duplicate prefixing adds bloat without benefit.

Tailwind CSS 4 Migration Workflow1. Audit2. Upgrade3. Config4. Fix UIVisual Regression CheckForms, rings, borders, dark modeStaging DeployVerify Vite build outputProductionMonitor Core Web Vitals
Step-by-step Tailwind CSS 4 migration workflow from codebase audit through staging and production verification
  1. Create a feature branch and snapshot key pages with screenshots.
  2. Run npx @tailwindcss/upgrade on a machine with Node.js 20+.
  3. Swap PostCSS for @tailwindcss/vite in vite.config.js.
  4. Rewrite app.css with @import, @theme, and @source.
  5. Build assets with npm run build and fix compiler errors.
  6. Walk through forms, modals, navigation, and dark mode manually.
  7. Deploy to staging before touching production traffic.

If you lack a dedicated front-end developer, a structured website redesign and revamp service can absorb the migration alongside template cleanup. Bundling both tasks avoids paying twice for the same Blade audit.

How does Tailwind CSS 4 configuration work without tailwind.config.js?

The @theme directive is the heart of CSS-first config. You declare design tokens as CSS custom properties. Tailwind generates matching utility classes automatically:

@import "tailwindcss";

@theme {
  --color-brand: oklch(55% 0.2 250);
  --color-brand-dark: oklch(40% 0.18 250);
  --font-display: "Inter", sans-serif;
  --breakpoint-3xl: 120rem;
}

These tokens produce utilities like bg-brand, text-brand-dark, and font-display. No JavaScript mapping required. The naming convention follows the pattern --{category}-{name}. Colors, spacing, fonts, breakpoints, and shadows all use the same model.

Extend or override defaults by redeclaring the variable. Remove a default token with @theme { --color-teal-*: initial; } syntax to reset an entire color family. That replaces the old corePlugins: false approach from version 3.

Custom utilities belong in CSS too. Use @utility for one-off classes that participate in variant chains:

@utility tab-4 {
  tab-size: 4;
}

Component classes still work through @layer components, though the Tailwind team encourages utilities over abstractions. On production Laravel apps I prefer small Blade components over large @apply blocks. Components survive upgrades better.

Legacy JavaScript config is supported via @config "../../tailwind.config.js" in your CSS file. Use this as a bridge, not a destination. Migrate tokens into @theme incrementally across sprints.

Mapping old theme.extend values

A config entry like colors: { brand: '#2563eb' } becomes --color-brand: #2563eb inside @theme. Spacing keys map to --spacing-*. Font families map to --font-*. The official Tailwind CSS upgrade guide lists every namespace. Bookmark it during the migration week.

How do you integrate Tailwind CSS 4 with Laravel and Vite 8?

Laravel 13 ships with Vite 8.x by default. PHP 8.3 is the minimum for that stack. Tailwind CSS 4 pairs cleanly with this toolchain. Remove tailwindcss from postcss.config.js and add the Vite plugin instead.

A typical Laravel 13 resources/css/app.css for a project like Adventure Third Pole Trek looks like this:

@import "tailwindcss";

@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';
@source '..//*.blade.php';
@source '..//*.js';

@theme {
  --color-primary: oklch(52% 0.15 160);
  --font-sans: "Figtree", ui-sans-serif, system-ui, sans-serif;
}

Livewire and Alpine class strings are scanned automatically when their file paths sit inside a declared @source glob. Dynamic class names built from JavaScript variables still fail detection. That limitation existed in version 3 too. Keep a safelist comment or static reference nearby if you rely on runtime-constructed classes.

Commit compiled assets if your production server lacks Node.js. Several sites I deploy through Deployer 7 build front-end assets in GitLab CI and commit the public/build directory. Tailwind CSS 4 produces smaller output files in most benchmarks, which helps page speed optimization scores.

Laravel + Vite + Tailwind CSS 4Blade Viewsresources/viewsapp.css@import @themeVite 8 Build@tailwindcss/viteOxide scan + compilepublic/buildHashed CSS + JSBrowser Delivery@vite directive in layout
Laravel 13 Vite 8 pipeline compiling Tailwind CSS 4 utilities from Blade templates and CSS theme tokens

WooCommerce and WordPress projects follow a different path. Those stacks often use Tailwind through theme build tools rather than Vite. See the WordPress development service page if your storefront still runs a classic theme pipeline. Mixing Tailwind CSS 4 into a jQuery-heavy legacy theme without a bundler creates more pain than gain.

For JSON config snippets during the migration, the JSON formatter tool helps validate generated theme exports before you paste them into CSS.

What breaking changes should you watch for during migration?

Breaking changes are manageable but visual. They will not always throw compiler errors. QA every form and focus state after upgrade.

Default border color. Version 3 applied border-gray-200 implicitly. Version 4 uses currentColor. Borders may disappear on elements that never set an explicit color. Add border-gray-200 where needed, or define a default in @layer base.

Ring utilities. Default ring width dropped from 3px to 1px. Focus rings on buttons and inputs look thinner unless you add ring-2 or a custom @theme override. Accessibility audits should include keyboard navigation checks after deploy.

Removed utilities. Deprecated aliases from version 2 finally disappeared. The upgrade tool rewrites most of them. Search your codebase for overflow-ellipsis, old flex shorthand, and opacity modifiers tied to removed color palettes. The regex tester helps batch-search Blade directories for stale class names.

Plugin ecosystem. Community plugins written for the JavaScript API may lag behind Oxide. Typography (@tailwindcss/typography) and forms (@tailwindcss/forms) have version 4 releases. Verify each plugin before upgrading production. A pattern I have seen repeatedly: one unmaintained plugin blocks the entire migration.

Browser support. Tailwind CSS 4 targets Safari 16.4+, Chrome 111+, and Firefox 128+. Older mobile browsers in some markets still matter. Check your analytics before assuming OKLCH and modern @property usage is safe. Provide fallbacks for critical brand colors if your audience skews older.

Breaking Changes Impact MapTailwind CSS 3border: gray-200 defaultring: 3px defaultJS config requiredTailwind CSS 4border: currentColorring: 1px defaultCSS @theme configForms + InputsVerify focus ringsCards + TablesCheck border colorsPlugin StackConfirm v4 support
Tailwind CSS 4 breaking changes — default border and ring shifts affect forms, cards, and plugin-dependent layouts

Dark mode syntax is unchanged for class-based toggling. dark:bg-gray-900 still works. If you used the legacy media strategy in JavaScript config, move it to a CSS variant declaration. Consult the official dark mode documentation for the v4 syntax.

Projects on Bootstrap 5 — common on legal-tech portals like Court Marriage In Nepal — may run Tailwind only on admin panels or new sections. Isolate Tailwind CSS 4 to the Vite-managed assets. Do not load two utility frameworks on the same element without a namespace prefix strategy.

When should you migrate versus stay on Tailwind CSS 3?

Not every project needs immediate migration. Tailwind CSS 3 remains functional. New features land in version 4 first. The decision depends on project age, team capacity, and release cadence.

  • Migrate now if you are already upgrading Laravel 12 or 13, Vite 8, or Node.js 26 LTS on CI.
  • Migrate now if build times block developer productivity on large monorepos.
  • Wait if a critical third-party plugin lacks Oxide support and has no fork.
  • Wait if the site enters a freeze period before Dashain or Tihar sales peaks.
  • Plan a hybrid bridge if only one module needs new Tailwind features — isolate it in a separate Vite entry.

Greenfield apps should start on version 4 directly. There is no reason to scaffold with deprecated directives in 2026. For e-commerce builds like Quick And Easy Nepalese Grocery, faster rebuilds during checkout UI iteration save real hours across a sprint.

Ongoing support and maintenance contracts should budget one to three days for a medium Laravel app migration. Complex design systems with custom plugins need longer. Document the @theme token file as the single source of truth going forward.

Cross-check your pipeline with the AI code review in CI guide if you want automated class-name linting after the upgrade. Static analysis catches dynamic class bugs earlier than manual QA alone.

Key Takeaways

  • Run npx @tailwindcss/upgrade first, then switch from PostCSS to @tailwindcss/vite on Vite 8 projects.
  • Replace @tailwind directives with @import "tailwindcss" and move tokens into @theme blocks.
  • Declare Laravel Blade, pagination, and compiled view paths with @source or styles will silently disappear.
  • Audit borders and focus rings — default border color and ring width changed between v3 and v4.
  • Verify every community plugin has a version 4 release before merging to production.
  • Commit built assets if production servers compile PHP only — the Oxide build still needs Node on CI.

People Also Ask

Is tailwind.config.js removed in Tailwind CSS 4?

It is optional, not removed. New projects configure design tokens in CSS through @theme. Existing JavaScript config files load via @config as a compatibility bridge. Plan to migrate tokens into CSS and delete the JS file once the bridge is empty.

Does Tailwind CSS 4 work with PostCSS only?

Yes. Install @tailwindcss/postcss if you cannot use the Vite plugin. The dedicated Vite integration is faster and recommended for Laravel 13 stacks. WordPress or legacy webpack setups may stay on PostCSS longer.

How long does a typical Tailwind CSS 4 migration take?

A small Laravel app with under fifty Blade files often completes in one to two days including visual QA. Large apps with custom plugins, multiple themes, or Bootstrap coexistence need a full sprint. The upgrade CLI handles most mechanical rewrites in minutes.

Can you use Tailwind CSS 4 with Bootstrap on the same page?

Technically yes, but avoid applying both frameworks to the same element. Prefix Tailwind utilities or scope them to a wrapper class. On legal-tech and portal projects I keep Bootstrap for the public site and Tailwind isolated to admin dashboards built with Vite.

Ship the upgrade with confidence

Tailwind CSS 4 What Changed and How to Migrate comes down to three moves: adopt the Oxide-powered build, rewrite configuration in CSS, and fix the small set of default visual changes that break silently. The upgrade CLI and Vite plugin remove most friction. Your time goes to QA, not config archaeology.

If you want help planning a Laravel, WordPress, or hybrid front-end upgrade, contact us for a migration assessment. We can audit your Blade templates, Vite pipeline, and plugin dependencies before you touch production. For related reading, explore essential Laravel packages, the testing and optimization service, and Mijar Law Associates for a live Laravel portal example.

Frequently Asked Questions

Oxide is Tailwind CSS 4's Rust-based compiler that replaced the JavaScript PostCSS pipeline from version 3. On large codebases, builds often finish in milliseconds instead of seconds. That speed matters when GitLab CI runs lint, test, and asset build on every push. Oxide reads CSS-native configuration through @theme and generates utilities via the @tailwindcss/vite or @tailwindcss/postcss integration packages.

No. It is optional, not removed. New projects define tokens in CSS via @theme. Existing JavaScript config loads through an @config compatibility bridge in your stylesheet.

One line: @import "tailwindcss"; in your main CSS entry file. The three separate @tailwind directives from version 3 are gone entirely.

Start on a clean git branch with a passing test suite and snapshot key pages. Run npx @tailwindcss/upgrade first; it rewrites config, updates imports, and flags deprecated utilities. The CLI requires Node.js 20 or newer, so CI runners must match. Uninstall tailwindcss, postcss, and autoprefixer, then install tailwindcss and @tailwindcss/vite. Swap the PostCSS chain for the Vite plugin in vite.config.js, rewrite app.css with @import, @theme, and @source directives, run npm run build, fix compiler errors, and QA forms, modals, navigation, and dark mode on staging before production.

@theme is the heart of CSS-first config in Tailwind CSS 4. You declare design tokens as CSS custom properties inside a @theme block, and Tailwind generates matching utility classes automatically. Colors use --color-brand, spacing uses --spacing-, fonts use --font-, and breakpoints use --breakpoint- naming. A value like --color-brand: oklch(55% 0.2 250) produces bg-brand and text-brand utilities. Override defaults by redeclaring variables, or reset an entire color family with syntax like --color-teal-: initial. Legacy theme.extend entries map directly: colors.brand becomes --color-brand in CSS.

Laravel 13 ships with Vite 8.x by default and requires PHP 8.3 minimum. Remove tailwindcss from postcss.config.js and add import tailwindcss from @tailwindcss/vite to vite.config.js alongside laravel-vite-plugin. Your resources/css/app.css should start with @import "tailwindcss", then declare @source globs for Blade views, JavaScript files, Laravel pagination templates under vendor/laravel/framework, and compiled views in storage/framework/views. Define brand tokens in @theme. Livewire and Alpine class strings scan automatically when their paths sit inside a declared @source glob. If production servers lack Node.js, commit compiled assets built in GitLab CI, a pattern I use with Deployer 7 deployments.

@source tells Tailwind CSS 4 which files to scan for utility class names. The Vite plugin detects content automatically for paths it knows about, but Laravel Blade views, pagination templates, and compiled views in storage/framework/views still need explicit @source declarations. I have missed compiled Blade paths on client projects and wondered why pagination styles vanished after deploy. Without those globs, classes used only in Blade or compiled PHP views are stripped from the final CSS silently, with no compiler error to warn you.

Yes. Install @tailwindcss/postcss if you cannot use the Vite plugin. The dedicated @tailwindcss/vite integration is faster and recommended for Laravel 13 stacks.

Two default shifts are visual, not compiler errors. Default border color changed from gray-200 in version 3 to currentColor in version 4, so borders on elements without an explicit color may disappear. Add border-gray-200 where needed or set a default in @layer base. Default ring width dropped from 3px to 1px, making focus rings on buttons and inputs look thinner unless you add ring-2 or a custom @theme override. QA every form and keyboard navigation path after upgrade. Deprecated aliases from version 2 are finally removed; the upgrade tool rewrites most, but search for overflow-ellipsis and old flex shorthand manually.

Ongoing support contracts should budget one to three days for a medium Laravel application migration. Complex design systems with custom plugins need longer. A structured workflow helps: feature branch, screenshots, run the upgrade CLI, swap Vite plugin, rewrite CSS entry, build, manual QA, then staging before production. Small projects with few custom plugins finish faster. Sites with unmaintained community plugins that block Oxide compatibility may need indefinite waiting or manual rewrites.

Tailwind CSS 3 remains functional, but new features land in version 4 first. Migrate now if you are already upgrading Laravel 12 or 13, Vite 8, or Node.js 26 LTS on CI, or if build times block developer productivity on large monorepos. Wait if a critical third-party plugin lacks Oxide support, or if the site enters a freeze period before Dashain or Tihar sales peaks. Greenfield apps in 2026 should start on version 4 directly. For e-commerce builds, faster rebuilds during checkout UI iteration save real hours across a sprint.

Remove the old chain with npm uninstall tailwindcss postcss autoprefixer. Install tailwindcss and @tailwindcss/vite. The core compiler lives in tailwindcss; bundler integration ships separately as @tailwindcss/vite or @tailwindcss/postcss. The official upgrade CLI is @tailwindcss/upgrade. Delete postcss.config.js unless you rely on other PostCSS plugins, because Autoprefixer is built into Tailwind CSS 4 and duplicate prefixing adds bloat without benefit. Community plugins like @tailwindcss/typography and @tailwindcss/forms have version 4 releases; verify each before upgrading production.

The most common cause on Laravel projects is missing @source paths for Blade templates, pagination views, or compiled files in storage/framework/views. Tailwind strips unused classes at build time, and classes referenced only in unscanned files never enter the output CSS. Dynamic class names built from JavaScript variables still fail detection, a limitation that existed in version 3 too. Keep a safelist comment or static reference nearby for runtime-constructed classes. Also check default border and ring changes that make elements look unstyled rather than missing entirely.

No. Autoprefixer is built into Tailwind CSS 4, so running both adds duplicate vendor prefixes and unnecessary bloat to your output files. Remove autoprefixer from npm and delete postcss.config.js unless your project uses other PostCSS plugins that still require that file. On Laravel 13 projects using the @tailwindcss/vite plugin, the entire old PostCSS chain for Tailwind is replaced by the dedicated Vite integration, which handles prefixing as part of the Oxide build.

Tailwind CSS 4 targets Safari 16.4 or newer, Chrome 111 or newer, and Firefox 128 or newer. OKLCH color formats, @property usage, container queries, and @starting-style depend on those baselines. Older mobile browsers in some markets still matter, so check your analytics before assuming modern color formats are safe for your entire audience. Provide fallbacks for critical brand colors if your user base skews older. Bootstrap 5 projects that run Tailwind only on admin panels should isolate version 4 to Vite-managed assets rather than loading two utility frameworks on the same element.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: