Skip to content
Tech Stack24 July 2026 · 7 min readUpdated 30 September 2026

Tailwind v4 in Production in 2026: Migration Strategy

This site still declares Tailwind ^3.4.18. Here is a verified plan for moving to v4: what the upgrade tool misses, the PostCSS rename, and a two-PR rollout that stays shippable.

Tailwind v4 in Production in 2026: Migration Strategy

What you'll learn

  • What Breaks When You Run the Tailwind v4 Upgrade Tool?
  • How Do You Run the Tailwind v3 to v4 Migration in Two Pull Requests?
  • Is Tailwind v4 Safe for Production in 2026?
Article contents

If your package.json still lists tailwindcss at version 3, the first move is small: create a throwaway branch, run npx @tailwindcss/upgrade, and read the diff before you decide anything else. This site is in that spot right now, with tailwindcss ^3.4.18 declared in its own package.json, so what follows is the plan I'd run on it, not a changelog summary. It suits the stack I recommend in the SaaS MVP stack guide: Next.js, TypeScript and the utility-first CSS approach Tailwind takes.

The stakes depend on what your CSS carries. Take a site like Pizzeria Bestek, which the case study puts at five to ten orders a day: a broken utility on the checkout page costs orders, which makes a styling change a business risk as well as a visual one. That is the reason to migrate in steps you can revert.

What Breaks When You Run the Tailwind v4 Upgrade Tool?

A woven vine sieve suspended in midair with a handful of water droplets slipping through a frayed gap and landing on a small glowing circuit board below

The tool rewrites what it can read statically, so the breakage sits in whatever it cannot see: class names built at runtime, options v4 dropped, and settings whose defaults changed. Tailwind's upgrade guide says the tool needs Node.js 20 or higher and is run with npx @tailwindcss/upgrade.

Dynamic class names and safelists

Tailwind scans your source as plain text. The official docs on detecting classes warn that it "has no way of understanding string concatenation or interpolation", so a class like bg-${color}-600 is never generated, in v3 or v4. If your components assemble class names from props, map each prop value to a complete class string before you migrate. The safelist option is a separate case: the v4 docs state that corePlugins, safelist and separator from a JavaScript config are not supported in v4.0, and the replacement is @source inline() in your CSS.

The PostCSS plugin rename

A living vine meeting an old corroded metal pipe at a mismatched coupling, glowing sap leaking from the seam between the two materials

In v3, postcss.config.js lists tailwindcss as a plugin. In v4, the PostCSS plugin lives in a dedicated @tailwindcss/postcss package, and imports and vendor prefixing are handled automatically, so postcss-import and autoprefixer can go. In a monorepo this is worth a search: a shared package whose PostCSS config still names tailwindcss will resolve a different entry point than your app does. That is my inference from how the rename works, so grep every postcss.config.* in the repo before you merge.

Renamed utilities and changed defaults

The renames are find-and-replace work. The changed defaults are the ones that fail quietly, because nothing throws an error. The table below lists the ones the guide documents.

Tailwind v3Tailwind v4
Configtailwind.config.js, detected automatically@theme in CSS; a JS config loads only through @config
PostCSS plugintailwindcss@tailwindcss/postcss
flex-shrink-* / flex-grow-*Validshrink-* / grow-*
shadow-sm / shadowSmall and default shadowsRenamed to shadow-xs / shadow-sm
Opacitybg-opacity-50bg-black/50
Default border colorgray-200currentColor
Gradient with a variant overrideResets the whole gradientOther stops are preserved
Custom utilities@layer utilities@utility

The border row is the one to test by eye. The guide says that border-* and divide-* used your gray-200 by default in v3 and now use currentColor, so any border without an explicit color changes appearance. The theme() function is also deprecated in favor of CSS variables, per the directives reference, which matters if your custom CSS calls it.

You don't have to move everything at once

Two directives let you migrate in stages. The directives reference says @config loads a legacy JavaScript config and @plugin loads a legacy JavaScript plugin, both kept for compatibility, and that they can be combined with @theme and @utility to move a project over incrementally. That gives you a sensible order: get the build green with @config pointing at your old file, then retire that file section by section, starting with theme tokens and ending with plugins. Each step is a small commit, so a bad one is easy to revert.

What to look for when you read the first diff

Read the raw output of PR one as a reviewer would, with three questions in mind. Did it touch every template folder you expect, including shared UI packages and Storybook or documentation apps? Did it leave any file with a class name it could not parse, which you will see as an unchanged flex-shrink-0 next to a renamed one? And did it change your lockfile in a way that pulls a second copy of Tailwind into a workspace? None of these has an automatic answer, and each one turns into a bug report if you skip it.

How Do You Run the Tailwind v3 to v4 Migration in Two Pull Requests?

Keep the mechanical output and the manual fixes in separate pull requests, so the first proves the tool's changes build and the second holds every decision that needs a human. This is my recommended process, not something the docs prescribe.

  1. PR one: the raw upgrade. Branch off main, run npx @tailwindcss/upgrade, and commit the output without hand edits. Let CI run.
  2. PR two: the manual fixes. Search for theme(, safelist, string-built class names and @layer utilities. Move custom utilities to @utility, and add explicit colors wherever you relied on the old border default.
  3. Look at the pages. Open your highest-traffic pages in a real browser, in light and dark mode and at phone width. A DOM snapshot test cannot see a color that changed.
  4. Deploy to a staging URL first. A wrong utility fails every page that uses it at once, which is why a preview deploy is cheap insurance.
  5. Remove leftovers last. Delete the old postcss-import and autoprefixer entries after the new build has been live for a while, since keeping them briefly makes a revert easier.

Where the typography plugin fits

Two intertwined vines growing from one tangled root system in a single pot, one vibrant with glowing cyan circuitry, the other pale and wilting with browning leaf edges

The plugin is compatible with both majors. The npm registry lists @tailwindcss/typography 0.5.20 with a peer range of tailwindcss >=3.0.0 || >=4.0.0, and the v4 docs load it from CSS with @plugin "@tailwindcss/typography";. If your tailwind.config.js still has it in a plugins array, move that line into your stylesheet and load the config file explicitly, or migrate the settings into CSS.

Make CI catch what the tool cannot

Add two cheap checks to the branch before you merge either pull request. First, run a production build, not only the dev server, because a missing class or a bad PostCSS entry can pass in development and fail in the pipeline. Second, write down the pages that define your product, such as pricing, sign-up, checkout and the main dashboard view, and open each one at desktop and phone width in both color schemes. A short written list turns "looks fine" into something a teammate can repeat. If you have end-to-end tests, run them against the staging deploy and not only against a local server, since the deployed build is the artifact your users will receive. None of this is specific to Tailwind, but a stylesheet change touches every page at once, which makes these checks worth more here than for a typical feature branch.

Is Tailwind v4 Safe for Production in 2026?

Yes for most projects, if your audience is on Safari 16.4, Chrome 111 or Firefox 128 and newer. The upgrade guide names those as the browsers v4 is designed for, because it relies on @property and color-mix().

Check your analytics for older versions before you pick a date. If a meaningful share of visitors is below those floors, staying on v3 is a legitimate choice, and it is still maintained: the npm registry has a v3-lts tag pointing at 3.4.19, published December 2025, while latest is 4.3.3, published on 16 July 2026.

What the speed numbers are

Tailwind's v4.0 announcement of 22 January 2025 reports Catalyst benchmarks: full builds dropped from 378ms to 100ms, and incremental rebuilds with no new CSS from 35ms to 192 microseconds. Those are Tailwind's numbers on their own project, so measure your build before and after and don't promise your team a figure.

Config as CSS

The old tailwind.config.js was a settings file. The @theme block makes your tokens CSS variables you can read at runtime, which is closer to a working design system than a build-time object. That is the part worth wanting once the migration itself is out of the way.

When staying on v3 is the right call

A migration is a cost, and a project with a small audience on older browsers, a plugin you cannot replace, or a release freeze has good reasons to wait. Staying is a decision you should write down with a date to revisit it, so it does not turn into an accident. The practical test is simple: if the upgrade tool's diff on a throwaway branch is small and your analytics clear the browser floor, migrate this quarter. If either fails, note which one, and check again when your constraint changes.

What to Do This Week

Run the upgrade tool on a throwaway branch and count the files it changes. Then search your repo for safelist, theme( and string-built class names, because that list is your second pull request. If you find a category of breakage I haven't listed, tell me what it was.

Free resource

Free SaaS MVP Scope Template

A Notion document with the full feature checklist, MVP vs. nice-to-have table, pre-build questions, and cost signals — so you walk into any developer call knowing exactly what to ask for.

Get the template →
DL

Dusko Licanin

Full-Stack Developer · Banja Luka, Bosnia

Full-stack developer shipping SaaS MVPs, web apps, and mobile apps using AI-augmented workflows — without agency coordination overhead. Live portfolio: BookBed, Callidus, Pizzeria Bestek.

Frequently Asked Questions

How do I migrate from Tailwind v3 to v4 without breaking production?

Split the work into two pull requests: one with the untouched output of the upgrade tool and one with the manual fixes it cannot make. Run npx @tailwindcss/upgrade on a throwaway branch, commit the result as is, and let CI build it. Then fix dynamic class names, replace safelist with @source inline(), move custom utilities to @utility, and add explicit border colors. Tailwind's upgrade guide says the tool needs Node.js 20 or higher. Deploy to a staging URL and check your key pages in a real browser before merging to main.

Is Tailwind CSS v4 safe for production in 2026?

Yes, for projects whose visitors use Safari 16.4, Chrome 111 or Firefox 128 and newer. The upgrade guide gives those as the browsers v4 is designed for, because it relies on @property and color-mix(). Check your analytics for older browser versions before choosing a migration date. If a meaningful share falls below the floor, stay on v3, which the npm registry still tags as v3-lts. The current latest tag is 4.3.3, published in July 2026, so v4 is actively released rather than experimental.

Do I still need tailwind.config.js in Tailwind v4?

No, v4 moves configuration into CSS with an @theme block, and a JavaScript config is no longer detected automatically. The directives reference says you can still load one with @config for backward compatibility, and legacy plugins with @plugin. Note that corePlugins, safelist and separator are not supported in v4.0 even through a loaded config file. That makes @config a useful stepping stone for an incremental migration, with the goal of retiring the file once your tokens live in CSS.

What changed with PostCSS in Tailwind v4?

The PostCSS plugin moved to a separate package named @tailwindcss/postcss, and the tailwindcss package no longer works as a PostCSS plugin. The upgrade guide also says imports and vendor prefixing are handled automatically, so postcss-import and autoprefixer can be removed. Update every postcss.config.* file in your repository, including those in shared packages of a monorepo, and run a production build to confirm nothing still references the old entry.

Does the @tailwindcss/typography plugin work with Tailwind v4?

Yes, the plugin supports both major versions. The npm registry lists @tailwindcss/typography 0.5.20 with a peer dependency range of tailwindcss >=3.0.0 || >=4.0.0. In v4 you load it from your stylesheet with @plugin "@tailwindcss/typography";, as the directives reference shows. If it currently sits in the plugins array of a JavaScript config, move that line into CSS or load the config with @config. Run a production build afterwards and look at your longest article page.