Custom Classes with @apply
Learn how to extract repeated utility combinations into reusable custom classes using the @apply directive in your CSS file.
Introduction
In the previous lesson you wrote text-sm font-medium text-gray-700 hover:text-blue-600 transition-colors on four different links. That repetition is normal in Tailwind, but sometimes you want a single, named class you can reuse — in markup, in a CMS-generated string, or in a third-party component you cannot easily edit. That is exactly what the @apply directive is for.
- What @apply does and how it differs from writing utilities directly in HTML.
- How to build a reusable class from a repeated utility combination.
- When @apply is the right tool, and when it works against Tailwind's design.
The Problem @apply Solves
Tailwind's whole philosophy is to style directly in markup instead of inventing new CSS class names. That works well most of the time, but a handful of situations genuinely call for a named class: a button style used across dozens of files, markup generated by a CMS where you cannot add utility classes, or overriding styles inside a third-party library's fixed class names.
<button class="px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700">Save</button><button class="px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700">Submit</button><button class="px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700">Continue</button>Three buttons, the same six-class string typed three times. If the design changes, you have to find and edit every occurrence.
Basic @apply Syntax
@apply is used inside a regular CSS file (typically your main input.css, alongside the @tailwind directives). You write a normal CSS selector, then @apply a list of utility classes inside it — Tailwind expands them into real CSS declarations at build time.
/* styles/input.css */@tailwind base;@tailwind components;@tailwind utilities;
.btn-primary { @apply px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700;}<button class="btn-primary">Save</button><button class="btn-primary">Submit</button><button class="btn-primary">Continue</button>Click Run to see what this code prints.
Notice that hover:bg-blue-700 correctly compiled into a real :hover selector — @apply understands Tailwind's variants, not just plain utilities.
Applying to the Navbar Links
Going back to the navbar from the previous lesson, you can collapse the repeated link classes into one reusable class.
.nav-link { @apply text-sm font-medium text-gray-700 hover:text-blue-600 transition-colors;}<a href="/" class="nav-link">Home</a><a href="/courses" class="nav-link">Courses</a><a href="/about" class="nav-link">About</a>Mixing @apply with Custom CSS
A class built with @apply is still plain CSS afterward, so you can add properties Tailwind has no utility for right alongside it — for example, a custom cubic-bezier curve or a vendor-specific property.
.btn-primary { @apply px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);}Using theme() in Custom CSS
When you need a design token in CSS that is not tied to a utility class — for example inside a media query or a raw calc() — the theme() function pulls values straight from your tailwind.config.js, keeping everything on the same scale.
.card { @apply rounded-lg shadow-md; border: 1px solid theme('colors.gray.200'); max-width: calc(100% - theme('spacing.8'));}When Not to Use @apply
It is tempting to reach for @apply everywhere once you know it exists, but doing so quietly rebuilds the exact class-soup problem Tailwind was designed to avoid — except now the styles live in a separate CSS file instead of next to the markup they affect. For one-off elements, plain utility classes in your HTML or JSX are almost always the better choice.
If a class combination appears in three or more unrelated places and rarely changes, @apply is reasonable. If it appears once, or changes often as you iterate on a design, keep the utilities inline.
Common Mistakes
- Reaching for @apply on the very first repetition instead of waiting until a pattern is genuinely stable.
- Applying variant utilities like hover: or md: expecting them to work outside of a real CSS selector context — they only work inside @apply, not as arbitrary strings.
- Forgetting that a class built with @apply cannot be overridden by later utility classes the same way two plain utilities can, because of CSS specificity and source order.
- Putting @apply rules in a random CSS file instead of the one processed by the Tailwind build pipeline, so they silently do nothing.
Best Practices
- Reserve @apply for small, stable, frequently repeated combinations like button or badge styles.
- Keep @apply classes short — if the list of utilities grows past six or seven, consider a component in your framework instead of a CSS class.
- Name classes after their role (btn-primary, card, nav-link), not their appearance, so the name still makes sense if the styling changes later.
- Place custom classes using @apply inside @layer components, which the upcoming lesson explains in detail.
Frequently Asked Questions
No, @apply is a CSS-file-only directive processed by Tailwind's build step (PostCSS). It cannot be used inside inline style attributes or CSS-in-JS template strings.
Yes, arbitrary value utilities work with @apply the same way normal utilities do.
It compiles to the same underlying CSS declarations either way, so the output size is comparable to writing the equivalent utilities directly — you are just trading duplication in HTML for duplication in the compiled stylesheet.
Key Takeaways
- @apply lets you compile a group of utilities into one reusable CSS class.
- It fully supports variants like hover: and responsive prefixes.
- theme() pulls values from your config into custom CSS outside of @apply.
- Overuse of @apply recreates the maintenance problems Tailwind's utility-first approach avoids — use it sparingly and deliberately.
Summary
You now know how to extract a repeated utility combination into a named, reusable class with @apply, and — just as importantly — when to leave utilities inline instead. Next, you will go one level deeper into customization by extending Tailwind's theme with your own colors, fonts, and spacing values.