Open-Source Wikis

/

Tailwind CSS

/

Packages

/

@tailwindcss/upgrade

tailwindlabs/tailwindcss

@tailwindcss/upgrade

Active contributors: Robin, Philipp, Jordan

Purpose

The migration tool. npx @tailwindcss/upgrade walks an existing v3 codebase and rewrites everything — CSS, templates, JS configs, PostCSS configs — into the v4 equivalents. It is a bin-publishing package.

Directory layout

packages/@tailwindcss-upgrade/
├── package.json                 # bin: ./dist/index.mjs
├── README.md
├── tsconfig.json
├── tsup.config.ts
└── src/
    ├── index.ts                 # CLI entry (~460 lines)
    ├── stylesheet.ts            # Stylesheet abstraction
    ├── index.test.ts
    ├── commands/
    │   └── help/
    │       └── index.ts
    ├── codemods/
    │   ├── config/
    │   │   ├── migrate-js-config.ts
    │   │   └── migrate-postcss.ts
    │   ├── css/
    │   │   ├── analyze.ts
    │   │   ├── format-nodes.ts
    │   │   ├── link.ts
    │   │   ├── migrate-at-apply.ts
    │   │   ├── migrate-at-layer-utilities.ts
    │   │   ├── migrate-config.ts
    │   │   ├── migrate-import.ts
    │   │   ├── migrate-media-screen.ts
    │   │   ├── migrate-missing-layers.ts
    │   │   ├── migrate-preflight.ts
    │   │   ├── migrate-tailwind-directives.ts
    │   │   ├── migrate-theme-to-var.ts
    │   │   ├── migrate-variants-directive.ts
    │   │   ├── migrate.ts
    │   │   ├── sort-buckets.ts
    │   │   └── split.ts
    │   └── template/
    │       ├── candidates.ts
    │       ├── is-safe-migration.ts
    │       ├── migrate-arbitrary-variants.ts
    │       ├── migrate-automatic-var-injection.ts
    │       ├── migrate-camelcase-in-named-value.ts
    │       ├── migrate-canonicalize-candidate.ts
    │       ├── migrate-handle-empty-arbitrary-values.ts
    │       ├── migrate-legacy-arbitrary-values.ts
    │       ├── migrate-legacy-classes.ts
    │       ├── migrate-max-width-screen.ts
    │       ├── migrate-modernize-arbitrary-values.ts
    │       ├── migrate-prefix.ts
    │       ├── migrate-simple-legacy-classes.ts
    │       ├── migrate-theme-to-var.ts
    │       ├── migrate-variant-order.ts
    │       ├── migrate.ts
    │       └── prepare-config.ts
    └── utils/
        ├── args.ts
        ├── git.ts                 # isRepoDirty
        ├── packages.ts            # detect package manager
        ├── version.ts             # installed/expected Tailwind version
        ├── renderer.ts
        └── write-file-safely.ts

Three codemod categories

graph TD
    Start[CLI entry] --> Cfg[Config codemods]
    Cfg --> CSS[CSS codemods]
    CSS --> Tpl[Template codemods]
    Tpl --> Done[Final write]

    subgraph "config/"
        MJC[migrate-js-config]
        MPC[migrate-postcss]
    end

    subgraph "css/"
        MTD[migrate-tailwind-directives]
        MAA[migrate-at-apply]
        MAL[migrate-at-layer-utilities]
        MIm[migrate-import]
        MMS[migrate-media-screen]
        MML[migrate-missing-layers]
        MP[migrate-preflight]
        MTV[migrate-theme-to-var]
        MVD[migrate-variants-directive]
        MC[migrate-config]
    end

    subgraph "template/"
        TMC[migrate-canonicalize-candidate]
        TLA[migrate-legacy-arbitrary-values]
        TLC[migrate-legacy-classes]
        TMP[migrate-prefix]
        TVO[migrate-variant-order]
    end

    Cfg --> MJC --> MPC
    CSS --> MTD --> MAA --> MAL --> MIm --> MMS --> MML --> MP --> MTV --> MVD --> MC
    Tpl --> TMC --> TLA --> TLC --> TMP --> TVO

The actual order is more nuanced — the entry point src/index.ts orchestrates them — but the rough flow is "rewrite configs first (so CSS can resolve them), rewrite CSS (so we know what utilities to look for), then rewrite templates (which need the up-to-date utility set)."

Key entry behaviors

The top-level run() in src/index.ts:

  1. Verifies the git worktree is clean (isRepoDirty() from utils/git.ts). The --force flag bypasses this.
  2. Detects the installed Tailwind version (utils/version.ts). Bails if node_modules is out of sync with package.json.
  3. Globs for stylesheets via globby and constructs Stylesheet objects (stylesheet.ts).
  4. Runs analyze, link, and migrate over the CSS in turn.
  5. Loads the v3 tailwind.config.js (if any) via prepare-config.ts and feeds the resulting design system into the template migrators.
  6. Globs source files (HTML, JSX, MDX, Vue, Svelte, etc.) via globby and runs each template codemod over them.
  7. Migrates the PostCSS config last (because the user is likely re-running PostCSS afterwards).
  8. Writes everything via write-file-safely.ts, which guards against killing the process mid-write (v4.2.3 shipped a fix for files being emptied if the process was interrupted, see CHANGELOG.md).

Safe migrations

packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts is a heuristic that decides whether a particular template token is safe to rewrite. The codemods only mutate strings the heuristic accepts. This is how the tool avoids breaking dynamically-constructed class names like bg-${color}-500.

The candidates passed through is-safe-migration come from the Rust scanner (@tailwindcss/oxide) — the upgrade tool reuses the same extractor the runtime build uses.

Canonicalization

The upgrade tool calls canonicalizeCandidate(...) (from packages/tailwindcss/src/canonicalize-candidates.ts) to produce the preferred shorthand form. The same logic backs the tailwindcss canonicalize CLI subcommand. Recent CHANGELOG entries describe lots of canonicalization improvements:

  • px-[1.2rem] py-[1.2rem]p-[1.2rem]
  • border-{t,b}-*border-y-*
  • start-fullinset-s-full
  • [&:has(…)]has-[…]
  • -mt-[20in]mt-[-20in]

Tests

Each codemod has a sibling *.test.ts with snapshot fixtures. The tool's top-level integration suite is in integrations/upgrade/.

Integration points

  • Depends on tailwindcss (for the canonicalizer + design system).
  • Depends on @tailwindcss/node (for Node-side compilation when probing the v3 config).
  • Depends on @tailwindcss/oxide (for the scanner, to know what candidates are present in templates).
  • Depends on globby, picocolors, postcss, postcss-import, postcss-selector-parser, prettier, semver, tree-sitter, tree-sitter-typescript, enhanced-resolve, dedent, mri, jiti. Tree-sitter is used to parse JavaScript ASTs for migrate-js-config.ts.

Entry points for modification

  • New CSS codemod: add a file under codemods/css/ and register it in the migration order inside src/index.ts.
  • New template codemod: add a file under codemods/template/ and register it in template/migrate.ts.
  • Tweak safety heuristics: edit template/is-safe-migration.ts. Lots of edge cases live here.
  • Tweak which file extensions are scanned: the globby patterns are constructed in src/index.ts.

The newest CHANGELOG entries for the upgrade tool include:

  • v4.2.3: don't migrate inline style attributes (PR #19918), preserve files when killed mid-write (PR #19846), use config.content when migrating, never migrate gitignored files, add .env/.env.* to default ignored content.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

@tailwindcss/upgrade – Tailwind CSS wiki | Factory