Upgrading to Tailwind CSS 4: A Quick Guide
Posted on: September 23 2025

Tailwind CSS has released version 4 and it brings with it many powerful new features. After recently upgrading to the latest version, the process was not as clear cut as I had initially thought, so I wanted to share some of they key challenges I faced through the upgrade process and how to resolve them.
In this post, I'll walk you through some of the essential changes to help upgrade your project with minimal friction, including:
- Upgrading dependencies and configurations
- What are the breaking changes?
- CSS variable-based theming
- The new way to define themes
- Switching themes
- How to use plugins in version 4?
On that note, let's take a look at how to upgrade Tailwind CSS to take advantage of the enhanced features in v4.
Breaking Changes
At a high level, there will be several breaking changes. The good news is that Tailwind CSS provides an upgrade tool that will do a bulk of the work, including:
- Upgrade package dependencies and plugins.
- Rename class utilities that have changed.
- Delete deprecated class utilities that are not longer used in v4.
- Sets new defaults that have changed.
- Updates the main CSS import with new directives.
- Imports the existing JavaScript config file into main CSS file since it is not automatically detected anymore.
Upgrade on a clean branch
It's best to do this on a clean branch, so you can review what's changed and make adjustments.
The tool will do a lot of pattern matching and it might change things inadvertently and so you'll need to check the UI to make sure it still looks as expected.
To run the upgrade process, use the command:
npx @tailwindcss/upgrade
- The tool will upgrade the needed dependencies as well as any plugins to compatible versions.
- Most of the popular plugins are already compatible with v4, so it makes this upgrade fairly straight forward.
Once the tool has completed running the upgrade, let's breakdown what's changed.
PostCSS configuration
The PostCSS configuration postcss.config.mjs
(or postcss.config.js
if using CommonJS) will have changed and be a little more streamlined.
In v3, it would look like this:
export default {
plugins: {
"postcss-import": {},
tailwindcss: {},
autoprefixer: {},
},
};
In v4, it only needs the following.
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
Updating Tailwind directives
The Tailwind upgrade tool will automatically update your main CSS file with the new directives used in v4.
In v3, the CSS directives are used as follows:
@tailwind base;
@tailwind components;
@tailwind utilities;
In v4, it has changed to:
@import 'tailwindcss';
This import will automatically set all of the inital base settings under the hood.
JavaScript config file changes
In v3, we're probably all familiar with the JavaScript config tailwind.config.js
file for defining theme details.
In v4, this file is still supported for backwards compatibility but now you'll need to explicitly use the @config
directive to use it in v4.
You will now see the following similar changes.
@import 'tailwindcss';
@config '../../../tailwind.config.js';
At first glance it may seem complex but this is a strategic move to make Tailwind v4 a CSS-only config approach.
The good news is that you can still use the JavaScript config and you may find in some cases you'll still need it as we'll see.
We'll take a look at some examples for defining themes in v4 later in this post but next let's look at what's changed in utility classes.
Deprecated and renamed utilities
The upgrade tool will find any of the class utilities that have been deprecated, renamed or changed in v4.
Deprecated utilities
Here's a couple examples of classes that have been deprecated or use a different notation.
v3 | v4 |
---|
opacity-20 | opacity/20 |
flex-grow-* | grow-* |
For a complete list of what's been deprecated click here.
Renamed utilities
Certain classes have been renamed to avoid confusion or to make things more explicit. Here's some examples.
v3 | v4 |
---|
shadow-sm | shadow-xs |
shadow | shadow-sm |
For a complete list of what's been renamed click here.
Updated class utility defaults
In v4, some of the default utility class values have been changed to use currentColor
as the default color over a standard default gray-200
.
I found these changes required the most refactoring for me since components can use a lot of borders and the tool may or may not highlight these changes.
So just make sure to double check the UI still looks the same and set an intended border color before saving your changes.
Here's a couple of examples to show how it's changed in v4.
v3 | v4 |
---|
border | border border-gray-200 |
divide | divide divide-gray-200 |
Thoughts on utility changes
After running the update tool on a couple projects, I found this step to be the most critical, since it will most likely have an impact on the UI look and feel.
Now that the breaking changes have been addressed, we can move on to some of the new directives for defining themes and plugins.
Creating themes in v4
Tailind v4 provides a new @theme
directive that can be used to define theme details. Defining theme keys is based on CSS variables.
Here's some of the common things you can do with themes in v4.
Using the theme directive
In v3, you might define custom colors as follows:
module.exports = {
theme: {
extend: {
colors: {
primary: 'oklch(0.8158 0.1597 76.85)',
secondary: 'oklch(0.5318 0.0367 226.95)',
},
},
},
};
To create a very simple theme, just use the @theme
directive and use the available namespaces and Tailwind will create utilities for them.
Here's a few of the namespaces available but I'll add a link to the full list below.
Namespace | Utility classes |
---|
--color-* | Color utilities like text-primary |
--font-* | Font family font-opensans , font-primary |
--text-* | Text size text-tiny , text-subtext |
--font-weight-* | font-heavy , font-bolder |
--breakpoint-* | Breakpoints sm:* , md:* |
For a complete list of theme namespaces look here.
Defining a basic theme
Here's a basic example of using the theme directive.
@import 'tailwindcss';
@theme {
--color-primary: oklch(0.8158 0.1597 76.85);
--color-primary-foreground: oklch(1 0 0);
--color-secondary: oklch(0.5318 0.0367 226.95);
--color-secondary-foreground: oklch(1 0 0);
--font-sans: "Open Sans", sans-serif;
}
The base color palette has been improved to include a wider range of steps from 50 to 950 and also some new colors.
Any colors inside the @theme
directive will extend the base color palette as usual.
To apply the theme is the same as it is in v3, just use the utility classes as usual.
<p class="text-primary-foreground font-sans">Tailwind v4 theme demo</p>
<button class="bg-secondary text-secondary-foreground">Click Here!</button>
Dynamic theme switching
Here's a common example of how to toggle between light and dark mode themes but it can be used for any number of themes you might define in your project.
By defining the default theme CSS variables in the root, it can be switched using an attribute or class name selector.
Tailwind provides the @custom-variant
directive to apply a custom selector theme variant.
@import 'tailwindcss';
:root {
--primary: oklch(0.8158 0.1597 76.85);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.5318 0.0367 226.95);
--secondary-foreground: oklch(1 0 0);
}
[data-theme='dark'] {
--primary: oklch(0.46 0 0);
--primary-foreground: oklch(0.91 0 0);
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
}
@theme {
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
}
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
To make the theme more dynamic it's best to take advantage of CSS variables to add flexibility.
In order to switch themes, you can set an attribute or a class selector dyanamically in your front-end HTML page to toggle the theme.
<html>
<body data-theme="dark">
</html>
Or, using a class selector example.
<html>
<body class="dark">
</html>
This technique can be used on the body tag for the whole page content or the variant can be applied to a single section as needed.
Now that we have the basic idea of how to work with themes, let's take a look at how to define and use plugins in v4.
Configuring plugins in v4
Since most websites using Tailwind are probably using some plugins, I'll show how to carry those over during the upgrade.
Keep in mind, that the plugins can remain in the tailwind.config.js
until you're ready to make the switch.
I'll configure the @tailwindcss/typography
and @tailwindcss/forms
as examples.
Plugin configuration in v3
In v3, the plugins were installed as follows in the tailwind.config.js
file. I'll add some plugin optional values to demonstrate how to use them in v4.
module.exports = {
theme: {
extend: {
plugins: [
require('@tailwindcss/typography')({
className: wysiwyg;
}),
require("@tailwindcss/forms")({
strategy: 'class',
}),
]
},
},
};
Plugin configuration in v4
In v4, a plugin can be installed as follows using the @plugin
directive.
@import 'tailwindcss';
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
Or, if you want to specify configuration options in the plugin, use the following notation.
@import 'tailwindcss';
@plugin "@tailwindcss/typography" {
className: wysiwyg;
}
@plugin "@tailwindcss/forms" {
strategy: 'class';
}
Customizing the typography plugin
I used the @tailwindcss/typography
plugin because it's a good example of where you might still need to use the tailwind.config.js
.
To fully customize the @tailwindcss/typography
classes, you'll need to modify the default styles in the theme extension, for example.
module.exports = {
theme: {
extend: {
typography(theme) {
return {
DEFAULT: {
css: {
fontSize: 'var(--text-lg)',
maxWidth: 'none',
code: {
fontSize: 'var(--text-base)',
},
blockquote: {
fontSize: 'var(--text-base)',
color: 'var(--text-blue-700)',
background: 'var(--text-blue-200)',
padding: '.5rem',
p: {
margin: '5px',
},
},
},
},
};
},
},
}
};
It's better to reference values using CSS variables to maximize flexibility.
Why you may still need the tailwind.config file?
Currently, I have found no other way customize the typography plugin styles using the v4 CSS-first configuration method.
So, without introducing any unneccesary CSS to customize a plugin's style output, it's more manageable to just keep using the tailwind.config.js
for now.
Final thoughts
Although this article is only covering the basic upgrade process, there's many benefits to using Tailwind v4.
Being able to define themes and configurations independently of a centralized tailwind.config.js
file, has many advantages in v4.
- It allows Tailwind to be used in the same way as any conventional CSS style sheet but powered by Tailwind.
- You can have separate style sheets with completely different themes and configurations and even completely different
tailwind.config.js
files too.
- It becomes more like Sass, with the ability import shared resources.
- It makes Tailwind CSS code more resusable and modular.
I hope this article has helped to shed some light on the upgrade process in some way. Tailwind is an awesome CSS alternative and worth staying up on the latest trends.