How and When To Use @apply in Tailwind

Search for a command to run...

No comments yet. Be the first to comment.
After a few days of using the 512GB MacBook Neo with TouchID, here are my impressions after some heavy, real-world use. I'll disclaim this review by saying that I consider myself a power user; on my m

2024 was a strange year for me that was a mix of trying new things, accomplishing the best work of my career, and stark transitions into unexplored territory. I think it's important to take a moment every once in a while to step back and look at one'...

What I've Been Up To Four Months Into Freelancing

Leaving My Job with No Plan — My New Career as a Freelancer

With a few glasses of wine in me and some nostalgia-fueled synthwave playing, I found myself reflecting on my past, and my history with computers. I got to thinking of my childhood, and more specifically, my personal history with computers. I love te...

If you've used CSS for long, you're likely used to creating CSS components for reusable styles. A big drawback of Tailwind appears to be code duplication — imagine having to add in all the classes for a button hundreds of times in an application: background colors, font colors, padding, margins, borders, and all the variants for hover, active, focus and so on. It would be maddening.
@apply, Our Savior! Maybe.Fortunately, Tailwind gives us a tool for this: @apply. This allows us to use existing Tailwind (and custom) classes and create CSS components of our own.
Here's a button class I've used:
@tailwind base;
@tailwind components;
@tailwind utilities;
.btn {
@apply inline-flex items-center justify-center
rounded
border-x border-t border-b
border-x-zinc-300/70 border-t-zinc-100 border-b-zinc-400/80
bg-gradient-to-b from-zinc-200 to-zinc-300
px-2 py-1
shadow
hover:text-opacity-100
focus:outline-none
focus:ring-2
focus:ring-blue-400/50
focus:ring-offset-0
focus:ring-offset-transparent
active:-translate-y-px
disabled:text-opacity-100
disabled:opacity-60
disabled:active:translate-y-0;
}
That's a lot of classes for a single element that is repeated many times. In this case, I have just applied the Tailwind classes and created my own component so I can simply use a class of .btn on anything that should be styled like a button.
You can even @apply .btn in another selector if you wanted, making @apply even more powerful. Imagine an inactive button that should look disabled, without disabling it, so it can still trigger an error message.
.btn-inactive {
@apply btn opacity-60;
}
@apply All the Things?@apply sounds really helpful — you can clean up all those messy classes cluttering up your HTML. But you should carefully weigh whether you actually need to create your own CSS component classes. After all, the whole point of Tailwind is that it gives you an easy, well-documented way to use a utility-first approach to designing CSS.
I have discussed the pros and cons of using Tailwind and its utility-first approach in another article, but to summarize, there are big advantages for maintainability by using Tailwind such as:
If you begin to use @apply all over the place, you've obviated all of those benefits where you've used them.
For these reasons, you ought to think twice before reaching for @apply; the majority of the time, there are better solutions. Here is a guide on when to use @apply.
If your frontend code uses components, it makes sense to use this feature in lieu of @apply. This way, you get to leverage the power and maintainability of utility-first design and combine that with the maintenance benefits and DRY-ness of extracting commonly used bits of code to components.
Components can even work for simple, single-elements like buttons or links, as you could have props or parameters you pass to give different styles and behaviours of buttons re-use a lot of code. But this is particularly true when you have more complicated components, like forms, tables, modals, etc. — this way you can combine your structural HTML with the style so that they are in one place that you go to make updates. That means that you can also build in the logic to make multiple variants. For example, you could have a "size" attribute that allows you to have large, medium, or small buttons.
See the following simplified example VueJS component: a radio button styled as a slider toggle.
<!-- An input with a few size options -->
<template>
<div class="flex items-start">
<span
class="relative inline-flex"
:class="{'h-4 w-8': size === 'sm', 'h-6 w-12': size === 'md', 'h-8 w-16': size === 'lg'}"
>
<input
:class="{'h-4 w-8': size === 'sm', 'h-6 w-12': size === 'md', 'h-8 w-16': size === 'lg'}"
class="peer absolute cursor-pointer rounded-full border-0 !bg-none outline-none checked:border-0 bg-zinc-300"
v-bind="$attrs"
type="checkbox"
/>
<label
:for="id"
aria-hidden="true"
:class="{'h-3 w-3 top-0.5 left-0.5 peer-checked:translate-x-4': size === 'sm', 'h-5 w-5 peer-checked:translate-x-6': size === 'md', 'h-7 w-7 peer-checked:translate-x-8': size === 'lg'}"
class="absolute top-0.5 left-0.5 transform cursor-pointer rounded-full bg-white shadow ring-0 duration-200"
>
</label>
</span>
</div>
</template>
<script>
export default {
props: {
size: {
type: String,
default: 'md',
},
},
}
</script>
We could have made a CSS component for the checkbox, but as this Vue component has a few HTML elements, and since my project uses it many times over, it makes sense to create a component. At this point, there will be no repetition of the used styles, so using @apply wouldn't make sense. Additionally, we can use Vue conditional classes to tweak several styles based on a specified size.
There would be many drawbacks and few advantages to @applying styles here. We can apply similar concepts to Laravel Blade components, React components, Angular, etc.
If you're not using a frontend framework like this (or Laravel), but you're just using vanilla PHP in a simple project, you could do similar extractions of components using PHP includes and such.
In many cases, we think that we are repeating a lot of exact Tailwind classes in a lot of places, but it often turns out that either
If there are differences, then herein lies the benefit of Tailwind - if there were differences between them, you might have to fight against a CSS component to get the changes you are looking for. In this case @apply isn't your friend.
Further, if the repetition occurs in one place or in a single file, you can use multi-cursor editing to select and edit all instances of this at once — this isn't particularly more difficult or time consuming than going back to a separate CSS file and editing a class then coming back. If you don't understand how to use multi-cursor editing, selecting several words at once, or selecting several lines at once in your IDE, you should definitely learn this. Let me know if you'd like a tutorial on this if you have no idea what I'm talking about!
Here's a perfect example of where multi cursor editing could allow you to adjust all these <img> elements with only a few keystrokes:
<div class="mt-3 flex -space-x-2 overflow-hidden">
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
<img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
</div>
<div class="mt-3 text-sm font-medium">
<a href="#" class="text-blue-500">+ 198 others</a>
</div>
In reality, most the "duplication of styles" isn't in your source code, the output of it that the browser receives. In the source code it lives as, loops in your backend or frontend code. So the apparent duplication is of little or no concern when it comes to maintaining your source code. So in these scenarios, it's not worth using @apply. You may think that this could at least reduce the payload to the browser, but in reality this sort or repetitive output compresses well and the actual difference to user experience is almost unmeasurable and thus isn't worth the maintenance burden it will incur.
At the end of the day, if you aren't solving a problem with @apply, then you're probably creating problems for no reason. If your code is fine the way it is and isn't actually more work to maintain, you're best leaving it and saving the extra weight and complexity in your application by avoiding creating extra classes you don't really need.
@apply!If you're not using a framework with components, you're not using a server-side language, you're mainly just creating a style for a single-element, that element is widely reused in exactly the same form over and over, and you're struggling with maintainability problems — then you can use @apply in good conscience. This is what it's for.
As a review, see this flowchart Tweeted by Adam Wathan, the creator of TailwindCSS.

I hope you found this article interesting. For more content like this, follow me on Twitter!