Tailwind CSS 4.0 was released on January 23, 2025. My project was using 3.x, and I decided to upgrade Tailwind as part of a migration from Next.js 14 to 15. If you're doing both, upgrade Tailwind and test it before moving to Next.js 15.
This guide walks through
migrating from Tailwind CSS 3.0 to 4.0
based on the official documentation. The upgrade brings substantial performance and productivity benefits.
Tailwind CSS v4.0 targets modern browsers. It supports Safari 16.4, Chrome 111, and Firefox 128. If you need to support older browsers, stay on v3.4.
Use the Automated Upgrade Tool ๐
Tailwind provides the
@tailwindcss/upgrade CLI to simplify the migration.
Before running it, check for uncommitted changes and save your work with
git stash or
git commit.
The tool handles these changes automatically:
Updates tailwindcss.
Removes or converts tailwind.config.js.
Converts postcss.config.js.
Updates classes in template files.
Converts configuration to use the theme API where needed.
The upgrade tool can handle many changes, but you still need to understand how to use Tailwind v4. Here's what changed.
Removal of @tailwind Directives ๐
In earlier versions, CSS files used @tailwind directives to load Tailwind CSS. In v4, import Tailwind directly in your CSS.
/* v3 */
/* @tailwind base; */
/* @tailwind components; */
/* @tailwind utilities; */
/* v4 */
@import "tailwindcss" ;
Remove the old @tailwind directives from your stylesheets.
Removal of Deprecated Utilities ๐
Utilities deprecated in v3 were removed. Use these replacements.
Before After Purpose bg-opacity-* Use opacity modifiers like bg-black/50 Set background opacity text-opacity-* Use opacity modifiers like text-black/50 Set text opacity border-opacity-* Use opacity modifiers like border-black/50 Set border opacity divide-opacity-* Use opacity modifiers like divide-black/50 Set divider opacity ring-opacity-* Use opacity modifiers like ring-black/50 Set ring opacity placeholder-opacity-* Use opacity modifiers like placeholder-black/50 Set placeholder opacity flex-shrink-* shrink-* Control flex shrinking flex-grow-* grow-* Control flex growth overflow-ellipsis text-ellipsis Truncate overflowing text with an ellipsis decoration-slice box-decoration-slice Slice box decorations across fragments decoration-clone box-decoration-clone Repeat box decorations for each fragment
Tailwind CSS v4.0 renames utilities for shadows, drop shadows, blur, backdrop blur, corner radii, outlines, and rings.
Before After Purpose shadow-sm shadow-xs Set shadow size shadow shadow-sm Set shadow size drop-shadow-sm drop-shadow-xs Set drop shadow size drop-shadow drop-shadow-sm Set drop shadow size blur-sm blur-xs Set blur size blur blur-sm Set blur size backdrop-blur-sm backdrop-blur-xs Set backdrop blur size backdrop-blur backdrop-blur-sm Set backdrop blur size rounded-sm rounded-xs Set corner radius rounded rounded-sm Set corner radius outline-none outline-hidden Hide the outline ring ring-3 Set ring width
Updated Shadow, Radius, and Blur Scales ๐
The default shadow, blur, and rounded scales were renamed so each utility has a named value. Older forms still work for compatibility, but update the old -sm utilities to -xs if you want to preserve their appearance.
{/* v3 ๋ฒ์ */}
< input class = "shadow-sm" />
{/* v4 ๋ฒ์ */}
< input class = "shadow-xs" />
{/* v3 ๋ฒ์ */}
< input class = "shadow" />
{/* v4 ๋ฒ์ */}
< input class = "shadow-sm" />
Changes to Outline Utilities ๐
The outline utility now includes both outline-width: 1px and outline-style: solid by default. Previously, outline applied only outline-style: solid. In v4, outline applies both outline-width: 1px and outline-style: solid.
{/* v3 ๋ฒ์ */}
< input class = "outline outline-2" />
{/* v4 ๋ฒ์ */}
< input class = "outline-2" />
The old outline-none utility didn't actually apply outline-style: none, so it was renamed to outline-hidden.
{/* v3 ๋ฒ์ */}
< input class = "focus:outline-none" />
{/* v4 ๋ฒ์ */}
< input class = "focus:outline-hidden" />
New Default Ring Width ๐
The default width of the ring utility changed from 3px to 1px.
{/* v3 ๋ฒ์ */}
< input class = "ring ring-blue-500" />
{/* v4 ๋ฒ์ */}
< input class = "ring-3 ring-blue-500" />
Gradients Preserve Stops When Combined with Variants ๐
Dark mode variants now preserve the
to-* setting. Use
via-none if you need to remove an intermediate stop.
After:
< div class = "bg-gradient-to-r from-red-500 via-orange-400 to-yellow-400 dark:via-none dark:from-blue-500 dark:to-teal-400" ></ div >
Changes to Container Configuration ๐
Options such as center and padding were removed from container configuration. Define them yourself.
@utility container {
margin-inline : auto;
padding-inline : 2rem;
}
New Default Border Color ๐
The default color for border-* and divide-* is now currentColor. To keep gray-200, use the following configuration.
@layer base {
* , ::after , ::before , ::backdrop , :: file-selector-button {
border-color : var ( --color-gray-200 , currentColor );
}
}
New Default Placeholder Color ๐
Placeholder text now defaults to the text color at 50% opacity. Add the following to keep the previous style.
@layer base {
input ::placeholder ,
textarea ::placeholder {
color : var ( --color-gray-400 );
}
}
New Default Button Cursor ๐
The default button cursor changed from pointer to default. Add the following to keep the previous behavior.
@layer base {
button :not ( :disabled ),
[ role = "button" ] :not ( :disabled ) {
cursor : pointer ;
}
}
Default Dialog Margin Removed ๐
@layer base {
dialog {
margin : auto ;
}
}
New Way to Register Custom Utilities ๐
Use @utility in place of @layer utilities.
@utility tab-4 {
tab-size : 4;
}
New Order for Stacked Variants ๐
Stacked variants now apply
from left to right
.
{/* v3 ๋ฒ์ */}
< ul class = "py-4 first:*:pt-0 last:*:pb-0" >
{/* v4 ๋ฒ์ */}
< ul class = "py-4 *:first:pt-0 *:last:pb-0" >
New Syntax for CSS Variables ๐
Use bg-(--color) in place of bg-[--color].
< div class = "bg-(--brand-color)" ></ div >
Hover Styles Apply Only on Devices That Support Hover ๐
You can register a custom variant if you need to preserve behavior on touch devices.
@custom-variant hover (&:hover);
Outline Color Is Included in Transitions ๐
< button class = "transition hover:outline-2 hover:outline-cyan-500" ></ button >
The corePlugins Option Was Removed ๐
You can no longer disable utilities with corePlugins.
Prefer CSS Variables to theme() ๐
.my-class {
background-color : var ( --color-red-500 );
}
4-18. Load JavaScript Configuration Explicitly ๐
@config "../../tailwind.config.js";
4-19. Access Theme Values from JavaScript ๐
You can use getComputedStyle to read CSS variables without resolveConfig.
let styles = getComputedStyle (document.documentElement);
let shadow = styles. getPropertyValue ( "--shadow-xl" );
Example of the New Configuration Structure ๐
@import 'tailwindcss' ;
@theme {
--color-primary: #1da1f2 ;
--font-sans: 'Inter', sans-serif ;
}
@utilities {
.btn {
@ apply px- 4 py- 2 rounded bg-primary text-white ;
}
}
Before and After Examples ๐
// Before (3.x)
theme : {
extend : {
colors : {
brand : '#3490dc' ,
},
},
}
// After (4.x)
@theme {
-- color - brand : #3490dc;
}
{/* Before (3.x) */}
< div class = "text-slate-900/80" ></ div >
{/* After (4.x) */}
< div class = "text-slate-900 text-opacity-80" ></ div >
7. Test After Migrating ๐
Check that styles work across all template paths.
Run npm run build and check for errors or warnings.
Confirm that the PostCSS configuration takes effect.
Test in both development and production environments.
Tailwind CSS 4.0
redesigns configuration, browser support, and day-to-day usage
. You can migrate safely and quickly by using the upgrade tool or reviewing the changes manually one by one.
The CSS-first configuration and focus on modern browsers offer advantages for future web development. I hope this post helps you plan your Tailwind upgrade.