Tailwind CSS v4: Functional Utilities

Tailwind CSS just released version 4, and it's a big one! This release is much more focused on using CSS standards over JavaScript, which have evolved over recent years. The switch to CSS-first has significantly improved developer performance with much faster build times. Plus many quality of life and minor tweaks.
One of the most exciting features in this release: functional utilities can now be written in CSS instead of JavaScript! Let's dive into how to use and create your own functional utilities.
1. Migrating to Tailwind CSS v4
Before you start using version 4, you'll need to upgrade your project. The Tailwind team has made this process as easy as possible with a new upgrade tool. If you're currently using v3, you can upgrade your project to v4 by running the command:
npx @tailwindcss/upgrade@next
2. What is a functional utility?
Functional utilities are a powerful feature in Tailwind CSS, they allow you to pass values and arbitrary values to your utilities. This allows you to create dynamic styles that can be re-used throughout your project.
Tailwind offers some functional utilities out of the box, such as:
text-*
— For styling font sizesborder-*
— For styling borders with width and colorrounded-*
— For styling border radius
What has changed in v4?
In previous versions of Tailwind CSS, to create a custom utility you would need to use JavaScript. However, in v4, you can now create custom utilities using CSS syntax!
Creating a functional utility in previous versions looked something like this:
import plugin from 'tailwindcss/plugin';
plugins: [
plugin(({ matchUtilities, theme }) => {
matchUtilities(
{
'text-stroke': (width) => ({
WebkitTextStrokeWidth: width
})
},
{
type: ['line-width', 'any'],
values: theme('borderWidth')
}
)
})
];
Here's how you can do this in v4:
@utility text-stroke-* {
-webkit-text-stroke-width: --value(integer)px;
-webkit-text-stroke-width: --value([*]);
}
Why use functional utilities?
- Flexibility: Create dynamic styles without writing custom CSS
- Type-safe: Built-in validation for values and colors
- Less code: Reduce the amount of CSS you write
- Dev experience: Great autocomplete support with Tailwind CSS IntelliSense
Be mindful to not add too much complexity to your project.
Understanding --value()
and --modifier()
When creating functional utilities, it's important to understand the two key functions:
--value()
: Uses the first value passed into the functional utility.text-*
: The*
is the value passed to the utility, such astext-2xl
ortext-white
/* Usage: typography-2
↑ value
↓ value placeholder */
@utility typography-* {
font-size: --value(integer)rem;
/* ↑ Requires value to be an integer */
}
--modifier()
: Uses the second value passed into the functional utility, following a/
.text-red/*
: The*
is the modifier, which can be used to specify a color, such astext-red/80
.
/* Usage: typography-2/red
↑ modifier */
@theme {
--color-red: #ff0000;
}
@utility typography-* {
font-size: --value(integer)rem;
color: --modifier(--color-*);
/* ↑ Requires modifier to be a specified color */
}
In this case the color would not be applied.
3. Creating a functional utility
Now that we understand what functional utilities are and why to use them, let's explore creating one. While Tailwind's built-in utilities are great, the real power of v4 comes from being able to craft custom utilities that fit your specific needs.
Let's look at an example — creating a text-stroke-*
utility, similar to Tailwind's border-*
utility. We'll build this step by step to explore how values, colors, and modifiers work together.
Basic utility
First, let's create a basic utility that adds a stroke to text OR a color, depending on the value passed.
/* Usage: text-stroke-2 text-stroke-white */
@utility text-stroke-* {
-webkit-text-stroke-width: --value(integer)px;
-webkit-text-stroke-color: --value(--color-*);
}
Supporting arbitrary values
We're off to a good start, but we can make this utility more flexible. To do this, we can add support for arbitrary widths and colors. Arbitrary values are enclosed in square brackets []
. This allows us to pass any value we want, such as a color or a width.
/* Usage: text-stroke-[2px] text-stroke-[green] */
@utility text-stroke-* {
-webkit-text-stroke-width: --value(integer)px;
-webkit-text-stroke-width: --value([*]);
-webkit-text-stroke-color: --value(--color-*, [color]);
}
Using a modifier
Let's add the option to use a modifier. This way we can more easily add a text stroke color at the same time! Modifiers are a great way to simplify your code.
/* Usage: text-stroke-2/white */
@utility text-stroke-* {
-webkit-text-stroke-width: --value(integer)px;
-webkit-text-stroke-width: --value([*]);
-webkit-text-stroke-color: --value(--color-*, [color]);
-webkit-text-stroke-color: --modifier(--color-*, [color]);
}
As you can see, we can easily create useful functional utilities with Tailwind CSS, and we don't have to touch JavaScript at all, it truly is paradise. 🏝️
--value()
and --modifier()
function parameters for a smoother development experience.
Final thoughts
The new way of creating functional utilities in Tailwind CSS v4 makes it easier than ever to create powerful and flexible styles for your website. It's definitely worth making the move to Tailwind CSS v4 if you haven't already!
Next Post

4 Reasons Why You Need a Website
Is your business missing out? Here's 4 reasons you need a website to attract more customers and stay competitive.