The official upgrade tool updates dependencies, converts tailwind.config.js into @theme variables and rewrites renamed classes. It needs Node.js 20 2,131 + and refuses to run with uncommitted Git 1,932 changes; run it on a new branch.
git switch -c tailwind-v4
npx @tailwindcss/upgrade≈ tailwindcss v4.3.3 │ ↳ Upgrading from Tailwind CSS `v3.4.19` │ ↳ Migrated configuration file: `.\tailwind.config.js` │ ↳ Migrated stylesheet: `.\src\input.css` │ ↳ Updated package: `tailwindcss` │ ↳ Migrated `.\src\index.html` │ Verify the changes and commit them to your repository.
On this test project the tool replaced the three @tailwind directives with @import 'tailwindcss';, moved the custom color into @theme { --color-brand: #0369a1; }, deleted the config file and rewrote the template:
<!-- v3 -->
<div class="rounded shadow bg-brand bg-opacity-50 !p-4">
<input class="border outline-none flex-grow ring">
<!-- v4, as migrated -->
<div class="rounded-sm shadow-sm bg-brand bg-opacity-50 p-4!">
<input class="border outline-hidden grow ring-3">| v3 | v4 |
|---|---|
| shadow-sm, rounded-sm | shadow-xs, rounded-xs |
| shadow, rounded | shadow-sm, rounded-sm |
| bg-opacity-50 | bg-black/50 |
| bg-[--brand] | bg-(--brand) |
| border: gray-200 | border: currentColor |
| ring: 3px, blue-500 | ring: 1px, currentColor |
Review the diff rather than trusting it blindly. The run above left two problems:
bg-opacity-50 survived unchanged but generates no CSS in v4. Search for -opacity- and rewrite by hand (here bg-brand/50).
The npm 2,036 script tailwindcss -i src/input.css -o dist/out.css now fails with "'tailwindcss' is not recognized", because v4's tailwindcss package has no command-line binary. Install @tailwindcss/cli and call that instead.
Then check the page in a browser: buttons now get cursor: default, hover: applies only on devices that can hover, and variants stack left to right (*:first:pt-0). A JavaScript config you keep must be loaded with @config, and its corePlugins and safelist options are no longer supported.