Upgrade Guide

Purpose and Scope

This page explains the Tailwind CSS v4 upgrade path from the perspective of a project maintainer moving an existing v3 application forward. The official upgrade guide presents v4 as a major release with necessary breaking changes, recommends the automated upgrade tool for most projects, and tells teams to review the resulting diff and test in a browser. In the repository, that user-facing workflow is implemented by the @tailwindcss/upgrade package, a Node executable that coordinates CSS, template, JavaScript configuration, and PostCSS configuration migrations from one command.

Sources: packages/@tailwindcss-upgrade/src/index.ts, packages/@tailwindcss-upgrade/package.json, packages/@tailwindcss-upgrade/README.md

The important mental model is that the upgrade tool is not a general formatter or a replacement for understanding the v4 changes. It is a codemod runner designed to perform the high-confidence edits Tailwind Labs can automate: finding relevant project files, preserving safety checks around dirty Git state, comparing expected and installed Tailwind versions, migrating stylesheet entry points, and rewriting common configuration formats. Complex projects can still need manual review, especially where configuration is dynamic, plugin setup cannot be statically understood, or templates construct class names in ways that are hard to transform safely.

Sources: packages/@tailwindcss-upgrade/src/index.ts, packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts, packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts

Relevant Source Files

  • packages/@tailwindcss-upgrade/src/commands/help/index.ts - renders command help, usage examples, invalid-command messages, and option descriptions using the shared terminal renderer.
  • packages/@tailwindcss-upgrade/src/index.ts - executable entry point for the upgrade command; parses flags, performs safety checks, discovers files, and orchestrates stylesheet, template, JavaScript config, and PostCSS migrations.
  • packages/@tailwindcss-upgrade/package.json - declares the published @tailwindcss/upgrade package, its executable bin, package version, build scripts, and migration-related dependencies.
  • packages/@tailwindcss-upgrade/README.md - package-level README that directs users to Tailwind’s full documentation, community support, and contribution guidance.
  • packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts - converts supported JavaScript Tailwind configuration into v4 CSS configuration concepts, including theme, content sources, dark mode, and plugin references.
  • packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts - rewrites common PostCSS configuration shapes to use @tailwindcss/postcss and removes now-unnecessary postcss-import and autoprefixer dependencies when it can do so safely.

Running the Upgrade Tool

For most applications, start from a clean branch and run the published executable through npm tooling: npx @tailwindcss/upgrade. The package metadata exposes the command through bin: ./dist/index.mjs, and the command itself is written as a Node executable. The official documentation also states that the tool requires Node.js 20 or higher, so check the runtime version before starting. The safest workflow is to commit or stash work, run the tool, inspect every changed file, install any dependency changes, and then exercise the application in the browser.

Sources: packages/@tailwindcss-upgrade/package.json, packages/@tailwindcss-upgrade/src/index.ts

The command has a deliberately small public option surface. --help or -h prints usage information, --version or -v prints the version number, --force or -f bypasses the clean-Git-directory guard, and --config or -c supplies a path to the Tailwind configuration file. The help renderer formats those options for the current terminal width, dims bracketed optional arguments, aligns aliases and flags, and prints an invalid command message when needed. That means the CLI contract is intended to be understandable directly from terminal output, not only from documentation.

Sources: packages/@tailwindcss-upgrade/src/index.ts, packages/@tailwindcss-upgrade/src/commands/help/index.ts

npx @tailwindcss/upgrade
npx @tailwindcss/upgrade --help
npx @tailwindcss/upgrade --force
npx @tailwindcss/upgrade --config ./tailwind.config.js

Execution Flow

When the executable starts, it treats the current working directory as the project base. Unless --force is present, it requires a clean Git directory and exits with a message telling the user to stash or commit changes before migration. It then reports the installed Tailwind version and checks for a mismatch between the version recorded in package.json and the version installed in node_modules. If those disagree, it asks the user to run the detected package manager’s install command before trying again, because codemods should run against the actual framework version in use.

Sources: packages/@tailwindcss-upgrade/src/index.ts

The stylesheet phase accepts explicit file arguments, resolving each relative to the project base. If no files are provided, it searches for **/*.css under the current directory, honors Git ignore rules, and skips node_modules to keep discovery fast and project-focused. The entry point imports CSS codemods for analysis, splitting, stylesheet linking, migration, bucket sorting, and node formatting, so the flow is structured as a pipeline rather than one monolithic text replacement. It also imports the Oxide Scanner, which connects the upgrade workflow to Tailwind’s class-detection infrastructure.

Sources: packages/@tailwindcss-upgrade/src/index.ts

Template migration is part of the same orchestration. The entry point imports prepareConfig and the template migrate codemod, which reflects the upgrade guide’s promise to handle changes in template files where possible. This matters because Tailwind generates CSS by scanning source files for complete class-name candidates. A migration that only rewrote CSS and config would leave many v3-to-v4 class changes for the user. The tool therefore prepares configuration context and then applies template-aware codemods after it has enough information about the project’s design system and sources.

Sources: packages/@tailwindcss-upgrade/src/index.ts

Configuration Migration

The JavaScript configuration codemod converts supported v3 config concepts into v4’s CSS-first configuration format. It loads the original config module, reads the source text, and returns either a structured migration result or null. A successful result contains content sources, static plugins, and generated CSS. A null result is not a failure of the whole upgrade; it means the config could not be automatically converted, so the stylesheet is updated to load the existing configuration file instead. This fallback is important for projects with dynamic JavaScript that a codemod should not guess about.

Sources: packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts

Inside the supported path, the codemod recognizes darkMode, content, theme, corePlugins, and simple static plugin declarations. It resolves theme data without applying plugins and presets, because those are handled separately, and it uses compatibility helpers from the core Tailwind package for theme values, keyframes, container utilities, dark mode behavior, escaping, and type inference. If corePlugins appears, the tool reports that the option is no longer supported as of Tailwind CSS v4.0 and removes it from the migrated configuration. Static plugin extraction records plugin path, base, and options for CSS-side representation.

Sources: packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts

PostCSS Migration

PostCSS migration handles the common configuration style used throughout Tailwind’s documentation: a plugins object containing entries such as postcss-import, tailwindcss, tailwindcss/nesting, and autoprefixer. In v4, the Tailwind PostCSS plugin lives in the dedicated @tailwindcss/postcss package, while imports and vendor prefixing are handled automatically by Tailwind’s new setup. The codemod therefore looks for simple PostCSS configs it can understand, replaces the old Tailwind plugin entry, and tracks whether it should add @tailwindcss/postcss or remove autoprefixer and postcss-import from package dependencies.

Sources: packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts

The detector uses an ordered strategy. It first looks for JavaScript PostCSS config files, then for a postcss field in package.json, and finally for JSON-based PostCSS config files. Once it has migrated a config, it writes changes through writeFileSafely, which keeps file output centralized instead of scattering raw writes throughout the codemod. If no PostCSS configuration is found, the command reports that it is skipping that migration. If dependency edits are needed, it asks the detected package manager helper to add @tailwindcss/postcss@latest or remove obsolete packages from the project.

Sources: packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts

// v4 PostCSS shape recommended by the installation docs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
}

Compact Reference

AreaConcrete namesBehavior
Commandnpx @tailwindcss/upgradeRuns the v4 migration executable from the published package.
Options--config/-c, --help/-h, --force/-f, --version/-vSupplies config path, prints help, bypasses clean-Git guard, or displays version.
Package executablebin: ./dist/index.mjsPublished command entry point for @tailwindcss/upgrade.
CSS discovery**/*.css, Git ignore aware, ignores **/node_modules/**Finds stylesheets when no explicit files are passed.
JS config resultnull or { sources, plugins, css }Converts supported config to CSS or falls back to loading the existing config.
PostCSS package@tailwindcss/postcss@latestAdded when a migrated PostCSS setup needs the v4 plugin package.
Removed packagesautoprefixer, postcss-importRemoved when the codemod determines they are obsolete in the migrated setup.

Review Checklist and Next Steps

After the tool finishes, review the generated diff as if it were a pull request from another developer. Confirm that stylesheet imports, generated theme CSS, content sources, plugin references, and PostCSS configuration match the intent of the old project. Run your package manager install command if dependencies changed, then start the app with the project’s normal development command. Pay special attention to pages with custom themes, dark mode variants, third-party plugins, and template classes that were previously assembled dynamically, because these are the areas most likely to require human judgment after the automated migration.

Sources: packages/@tailwindcss-upgrade/src/index.ts, packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts, packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts

For related reading, continue with the tailwindcss Package API page to understand the v4 compiler being targeted, Configuration and Plugin API for the compatibility layer behind migrated configs, PostCSS Plugin for the new @tailwindcss/postcss package, and Detecting Classes in Source Files for why complete class names matter during template review.