Migrating from Tailwind v3 to v4

Advanced
13 min

Migrating from Tailwind v3 to v4

Tailwind v4 is a rewrite: a new engine, CSS-first configuration, renamed utilities and changed defaults. Most projects migrate in an afternoon because an official tool does the mechanical work, but a few behaviour changes must be checked by hand. In this lesson you will run the upgrade tool, understand the changes it makes, and work through the checklist of things it cannot detect.

Run the upgrade tool

Commit or stash your work, create a branch, and run the tool from the project root:

bash
git switch -c tailwind-v4 npx @tailwindcss/upgrade

It requires Node.js 20 or later. The tool updates dependencies, rewrites tailwind.config.js into @theme CSS, replaces the @tailwind directives with @import "tailwindcss", switches PostCSS to @tailwindcss/postcss, and renames utilities in your templates. Review the diff before continuing; the rewrite is conservative and leaves comments where it needs a decision.

Imports and configuration

The manual equivalent of what the tool does to your stylesheet:

css
/* v3 */ @tailwind base; @tailwind components; @tailwind utilities; /* v4 */ @import "tailwindcss"; @theme { --color-brand: #2563eb; /* was theme.extend.colors.brand */ --font-sans: "Inter", sans-serif; --breakpoint-3xl: 120rem; /* was theme.extend.screens */ }

Anything the tool cannot express in CSS stays in the config file, loaded with @config "./tailwind.config.js"; — treat that as temporary. Plugins become @plugin "@tailwindcss/typography";, class-based dark mode becomes @custom-variant dark (&:where(.dark, .dark *));, and a prefix becomes prefix(tw) on the import with classes written tw:flex. Sass, Less and Stylus are not supported; v4 handles nesting and imports itself.

Renamed and removed utilities

The scale for shadows, radii, blurs and rings shifted one step:

| v3 | v4 | |---|---| | shadow-sm / shadow | shadow-xs / shadow-sm | | rounded-sm / rounded | rounded-xs / rounded-sm | | blur-sm / blur | blur-xs / blur-sm | | ring (3px) | ring-3 | | outline-none | outline-hidden | | bg-gradient-to-r | bg-linear-to-r | | !flex | flex! | | bg-[--brand] | bg-(--brand) |

Removed entirely: the *-opacity-* utilities (use /50 modifiers), flex-shrink-*/flex-grow-* (use shrink-*/grow-*), overflow-ellipsis (text-ellipsis) and decoration-slice (box-decoration-slice). The upgrade tool rewrites all of these.

Behaviour changes to check by hand

These do not show up as errors; they change how existing markup looks:

  • Default border color is now currentColor, not gray-200. Bare border and divide-y need an explicit color, or a global default in @layer base.
  • Default ring is 1px currentColor instead of 3px blue; focus styles that relied on ring need ring-3 ring-blue-500.
  • Variant stacking applies left to right: first:*:pt-0 becomes *:first:pt-0.
  • hover: applies only on devices that support hover; restore the old behaviour with @custom-variant hover (&:hover); if required.
  • Preflight tweaks: placeholder text uses the current color at 50% opacity, buttons use cursor: default, and dialog margins are reset.
  • container no longer reads the center and padding options. Recreate them as a utility:
css
@utility container { margin-inline: auto; padding-inline: 2rem; }

Verify and clean up

Build with both versions and compare output size and screenshots of key pages. Search for what the tool cannot see: class names assembled in JavaScript, classes in CMS content, and any old safelist, which becomes @source inline(). Finally delete tailwind.config.js once @config is unnecessary, remove autoprefixer and postcss-import (both built in), and drop plugins that are now core features.

Common mistakes

  • Running the tool on a dirty working tree, which makes the diff impossible to review.
  • Forgetting that borders and rings changed color and width, then shipping invisible dividers.
  • Leaving bg-opacity-* classes in JavaScript-built strings where the tool could not rewrite them.
Quick Quiz
Question 1 of 2

What does the v3 utility `shadow` become in v4?

Key Takeaways

  • npx @tailwindcss/upgrade on a clean branch handles dependencies, config-to-CSS conversion and utility renames.
  • @tailwind directives become @import "tailwindcss" and tailwind.config.js becomes @theme; @config bridges the rest.
  • Shadows, radii, blurs and rings shifted one step, and !, bg-[--var] and gradient names changed form.
  • Check by hand: border color, ring width, hover on touch, variant order, container and Preflight tweaks.
  • Replace old safelist entries with @source inline() and remove plugins that are now built in.

Next lesson: Building a Complete Landing Page with Tailwind — combine everything from the course into a polished, responsive landing page.

Migrating from Tailwind v3 to v4 - Tailwind CSS | CodeYourCraft | CodeYourCraft