LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2416 min read

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 You Will Learn
  • 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>
Compiled CSS (simplified)

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.

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.

A Rule of Thumb

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

Avoid These 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.

Next Lesson →

Extending the Theme