Upgrade Tool
Purpose and Scope
The @tailwindcss/upgrade package is the first-party migration command for moving Tailwind CSS projects from v3 to v4. The official upgrade guide positions it as the recommended starting point for most projects: run it in a fresh branch, let it perform the bulk of dependency, configuration, stylesheet, and template updates, then review the diff and test the result in a browser. That workflow matters because v4 changes how projects are configured and integrated, including the move to CSS-first configuration and dedicated integration packages for tools like PostCSS and Vite.
The repository implementation reflects that purpose: the package is published as a command-line package with a bin entry, and the executable src/index.ts coordinates project checks, stylesheet migration, JavaScript and PostCSS configuration migration, and template class migration. The tool is intentionally conservative at the command boundary. Unless --force is supplied, it refuses to operate on a dirty Git working tree, which matches the documentation guidance to use a branch and review generated changes instead of treating the migration as an opaque in-place rewrite.
Sources: packages/@tailwindcss-upgrade/package.json, packages/@tailwindcss-upgrade/src/index.ts
Relevant Source Files
packages/@tailwindcss-upgrade/package.jsondefines the published package name, version, executablebin, build scripts, public files, and dependencies used by the migration command.packages/@tailwindcss-upgrade/src/index.tsis the command entry point. It parses flags, renders help, checks repository state and installed versions, discovers CSS files, and imports the codemod families used during migration.packages/@tailwindcss-upgrade/src/commands/help/index.tsrenders terminal help text, including usage wrapping, option alignment, invalid-command output, and the shared Tailwind header.packages/@tailwindcss-upgrade/src/codemods/css/migrate.tsexposes the CSS migration pipeline andMigrateOptionscontract used when rewriting stylesheets.packages/@tailwindcss-upgrade/src/codemods/template/migrate.tsexposes candidate-level and file-content template migration functions, the default migration sequence, and safety and validation checks for class rewrites.
Command Contract
The package metadata names the npm package @tailwindcss/upgrade and publishes only built output under dist, with the command executable resolved through ./dist/index.mjs. For users, the documented invocation is simple: run npx @tailwindcss/upgrade from the project root. For operators and contributors, the metadata is also useful because it shows that the package is built with tsup-node, linted with TypeScript, and depends on the same monorepo Tailwind packages that the migration logic needs to understand v4 syntax and candidate canonicalization.
npx @tailwindcss/upgrade
npx @tailwindcss/upgrade --help
npx @tailwindcss/upgrade --force
npx @tailwindcss/upgrade --config ./tailwind.config.jsThe command-line options are declared in the entry point rather than inferred from documentation. --config accepts a string path and has the alias -c; --help is a boolean flag with alias -h; --force is a boolean flag with alias -f; and --version is a boolean flag with alias -v. Positional arguments are treated as candidate input files for the stylesheet migration path. If no stylesheet files are provided, the command searches the current working directory and subdirectories for CSS files while honoring Git ignore rules and skipping node_modules during discovery.
Sources: packages/@tailwindcss-upgrade/package.json, packages/@tailwindcss-upgrade/src/index.ts
Execution Flow
At startup, src/index.ts establishes the project root from process.cwd(), prepares Git ignore handling through globby, prints the shared header, and initializes a cleanup list. The first significant guard is repository safety: when --force is absent, isRepoDirty() is called and a dirty working tree produces an error, explanatory hint, and process exit. This is not just convenience output; it is a workflow constraint that prevents automated codemods from mixing with unrelated local changes and making review harder.
After the Git guard, the command reports the installed Tailwind CSS version and compares it with the expected version recorded for the project. If the version installed in node_modules does not match the expected package version, the command prints a structured diff-like message showing the expected and installed values and tells the user to run the detected package manager install command before retrying. This check reduces false migrations caused by stale dependencies, which is especially important because template class migration relies on the design system and canonicalization behavior supplied by the installed framework version.
The stylesheet phase begins by resolving any positional arguments against the project root. When no files are provided, it announces a CSS search and calls globby(['**/*.css'], { cwd: base, absolute: true, gitignore: true, ignore: ['**/node_modules/**'] }). The visible imports show that the entry point then has access to stylesheet analysis, linking, splitting, sorting, formatting, migration, and safe writing utilities. In practice, this makes the command more than a text replacement script: it constructs enough project context to decide which CSS files can be migrated and how their Tailwind directives and configuration links should be transformed.
Sources: packages/@tailwindcss-upgrade/src/index.ts
CSS Codemod Pipeline
The CSS migration API is centered on migrate(stylesheet, options) and migrateContents(stylesheetOrString, options, file). MigrateOptions carries the data that stylesheet transforms need: newPrefix, the compiled designSystem, the resolved user configuration, the configuration file path, and any JavaScript configuration migration result. The public guard in migrate rejects stylesheets without a file path and exits early when stylesheet.canMigrate is false, so the pipeline can be called from broader analysis code without assuming every discovered stylesheet should be rewritten.
Inside migrateContents, strings are normalized into a Stylesheet object when needed, and the actual work is expressed as an ordered PostCSS plugin chain. The order is significant because each transform prepares the tree for later transforms: imports are migrated first, then @apply, screen media, variant directives, utility layers, missing layers, Tailwind directives, config references, preflight handling, and theme-to-variable conversion. This sequence mirrors the v4 migration problem space described by the official guide, where legacy directives and JavaScript configuration increasingly move toward CSS-native declarations and v4-compatible semantics.
Compact CSS migration reference:
| Export | Input | Behavior |
|---|---|---|
MigrateOptions | prefix, design system, config, config path, JS config migration | Shared context for CSS transforms. |
migrate(stylesheet, options) | Stylesheet with a file path | Skips non-migratable stylesheets and delegates to content migration. |
migrateContents(stylesheet, options, file?) | Stylesheet or CSS string | Runs the ordered PostCSS migration plugin pipeline and returns the processed result. |
Sources: packages/@tailwindcss-upgrade/src/codemods/css/migrate.ts
Template Codemod Pipeline
Template migration works at the Tailwind class-candidate level. The file defines a Migration type that receives a DesignSystem, an optional user config, and a raw candidate string, then returns the migrated candidate synchronously or asynchronously. DEFAULT_MIGRATIONS is the ordered family of candidate transforms: empty arbitrary values, prefix changes, canonicalization, simple legacy classes, camel-cased named values, broader legacy classes, max-width screen changes, variant order, automatic variable injection, legacy arbitrary values, and modernized arbitrary values. The comment on variant order notes that it must run before migrations that modify variants, making the array itself part of the behavioral contract.
The template pipeline also avoids rewriting everything blindly. migrateCandidate accepts an optional source location, and when a location is present it calls isSafeMigration before applying cached migrations. The cached migration path prepares design-system storage, creates signature options, runs each default migration, canonicalizes the final candidate, and then validates that the resulting candidate has a utility signature. If validation fails, the original candidate is returned. This preserves user intent in ambiguous situations and prevents a parseable but invalid partial utility from being transformed into a misleading v4 class.
The default export migrates full file contents by extracting raw candidates for a given extension, accumulating StringChange records, and later splicing changes into the original string. Even though the detailed loop is truncated in the supplied evidence, the visible function boundary establishes the API shape used by the command: the caller supplies a design system, optional config, file contents, and extension, and receives rewritten contents. That design lets the CLI apply the same class migration logic across template languages while still using extension-aware extraction.
Sources: packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts
Help Rendering and Operator Guidance
The help command is a small but important part of the public interface because it is what users see when they need confirmation before running a destructive-looking migration. It renders the shared header, optionally prints an invalid command message, formats usage examples, and wraps long option lists according to the current terminal width. It also aligns aliases and long flags so options remain readable in narrow terminals. Those details are grounded in src/commands/help/index.ts, which treats usage output as structured terminal UI rather than a static string.
For a safe migration run, start by updating to an environment compatible with the official guide, create a branch, install dependencies so package and installed versions match, then run the command without --force. Use --config only when the project needs to point at a specific legacy configuration file, and reserve --force for situations where the dirty-tree guard is intentionally too strict. Afterward, inspect stylesheet changes, template class rewrites, and package or PostCSS configuration changes together, because the codemod families are designed to coordinate those surfaces rather than migrate each one in isolation.
Sources: packages/@tailwindcss-upgrade/src/commands/help/index.ts, packages/@tailwindcss-upgrade/src/index.ts
Related Pages
Continue with upgrade-guide for the human migration checklist, configuration-and-plugin-api for the configuration concepts that the tool migrates away from or preserves, postcss-plugin for v4 PostCSS setup after migration, and tailwindcss-package-api for the compiler and design-system APIs that underpin candidate canonicalization.