Trendy Coder Logo
  • Home
  • About
  • Blog
  • Newsletter

Upgrading to Tailwind CSS 4: A Quick Guide

Posted on: September 23 2025
By Dave Becker
Hero image for Upgrading to Tailwind CSS 4: A Quick Guide

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;

/* other Tailwind CSS here*/

In v4, it has changed to:

@import 'tailwindcss';

/* other Tailwind CSS here*/

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';

/* path to tailwind.config file */
@config '../../../tailwind.config.js';

/* other Tailwind CSS here*/

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.

v3v4
opacity-20opacity/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.

v3v4
shadow-smshadow-xs
shadowshadow-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.

v3v4
borderborder border-gray-200
dividedivide 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)',
        // etc.. 
      },
    },
  },
};

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.

NamespaceUtility 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; // custom class prefix
        }),
        require("@tailwindcss/forms")({
          strategy: 'class', // only generate classes
        }),
      ]
    },
  },
};

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.

Topics

SEOLinuxSecuritySSHEmail MarketingMore posts...

Related Posts

Hero image for How to Style Form Inputs with Tailwind CSS Plugin
Posted on: July 11 2024
By Dave Becker
How to Style Form Inputs with Tailwind CSS Plugin
Hero image for How does Astro Content Framework support SEO?
Posted on: July 10 2024
By Dave Becker
How does Astro Content Framework support SEO?
Hero image for Server-Side Pagination Made Easy in Payload CMS
Posted on: August 11 2025
By Dave Becker
Server-Side Pagination Made Easy in Payload CMS
Hero image for How Does Tailwind CSS Support Theming?
Posted on: July 16 2024
By Dave Becker
How Does Tailwind CSS Support Theming?
Hero image for Uploading Made Easy: Exploring Media Features in Payload CMS
Posted on: September 23 2025
By Dave Becker
Uploading Made Easy: Exploring Media Features in Payload CMS
Hero image for How to Overlay Text on an Image Using Tailwind CSS
Posted on: July 20 2024
By Dave Becker
How to Overlay Text on an Image Using Tailwind CSS
Hero image for Top 5 SEO Optimization Tips in Next.js 14
Posted on: June 11 2024
By Dave Becker
Top 5 SEO Optimization Tips in Next.js 14
Hero image for How To Create an Email Opt-In Form Using AWeber
Posted on: June 28 2024
By Dave Becker
How To Create an Email Opt-In Form Using AWeber
Trendy Coder Logo
Resources
  • Blog
Website
  • Home
  • About us
Subscribe

Get the latest news and articles to your inbox periodically.

We respect your email privacy

© TrendyCoder.com. All rights reserved.