The Problem @apply Solves
As your project grows, you might notice the same combination of utility classes repeated across many elements. For example, every button might use bg-indigo-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-indigo-700. Copying this everywhere is error-prone.
The @apply directive lets you extract these utility patterns into a custom CSS class while keeping all the benefits of the Tailwind utility system.
Using @apply
In your CSS file (usually src/input.css), you can create custom classes that compose Tailwind utilities:
@tailwind base;
@tailwind components;
@tailwind utilities;
.btn {
@apply font-semibold py-2 px-4 rounded-lg transition-colors duration-200;
}
.btn-primary {
@apply bg-indigo-600 text-white hover:bg-indigo-700;
}
.btn-secondary {
@apply bg-gray-200 text-gray-800 hover:bg-gray-300;
}
.btn-danger {
@apply bg-red-600 text-white hover:bg-red-700;
}
.btn-outline {
@apply border-2 border-indigo-600 text-indigo-600 hover:bg-indigo-600 hover:text-white;
}
Now your HTML becomes much cleaner:
<!-- Before @apply -->
<button class="bg-indigo-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-indigo-700">
Save
</button>
<!-- After @apply -->
<button class="btn btn-primary">Save</button>
<button class="btn btn-secondary">Cancel</button>
<button class="btn btn-danger">Delete</button>
<button class="btn btn-outline">Learn More</button>
The @layer Directive
Tailwind processes styles in three layers in order: base, components, and utilities. Using the @layer directive tells Tailwind exactly where to place your custom styles, ensuring proper specificity.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
/* Reset / global styles */
h1 {
@apply text-3xl font-bold;
}
h2 {
@apply text-2xl font-semibold;
}
a {
@apply text-indigo-600 hover:text-indigo-800;
}
}
@layer components {
/* Reusable component classes */
.card {
@apply bg-white rounded-xl shadow-md p-6;
}
.badge {
@apply inline-flex items-center text-xs font-semibold px-3 py-1 rounded-full;
}
.badge-success {
@apply bg-green-100 text-green-800;
}
.badge-error {
@apply bg-red-100 text-red-800;
}
}
@layer utilities {
/* Custom one-off utilities */
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.text-shadow-lg {
text-shadow: 4px 4px 8px rgba(0, 0, 0, 0.2);
}
}
Why @layer Matters
- @layer base — Styles that apply to raw HTML elements (like
h1,a,body). These have the lowest specificity. - @layer components — Reusable component classes like
.card,.btn. These can be overridden by utilities. - @layer utilities — Custom one-off utilities. These have the highest specificity and will always win.
Putting .btn in @layer components means you can still override it with utilities:
<!-- The px-8 utility overrides the px-4 from .btn -->
<button class="btn btn-primary px-8">Extra Wide Button</button>
Real-World Example — Form Styles
@layer components {
.form-input {
@apply w-full px-4 py-2 border border-gray-300 rounded-lg
text-gray-900 placeholder-gray-400
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent
transition-shadow duration-200;
}
.form-label {
@apply block text-sm font-medium text-gray-700 mb-1;
}
.form-group {
@apply mb-5;
}
.form-error {
@apply text-sm text-red-600 mt-1;
}
}
<form class="max-w-md mx-auto">
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" class="form-input" placeholder="you@example.com">
</div>
<div class="form-group">
<label class="form-label">Password</label>
<input type="password" class="form-input" placeholder="••••••••">
<p class="form-error">Password must be at least 8 characters.</p>
</div>
<button type="submit" class="btn btn-primary w-full">Sign In</button>
</form>
When to Use @apply
Good use cases:
- Buttons, form inputs, and badges that appear dozens of times.
- When using a templating system that does not support component abstraction.
- Base styles for headings and links.
When to avoid @apply:
- If you are using a component framework (React, Vue, Svelte) — extract a component instead. The component encapsulates both HTML and styles.
- For one-off styles that appear only once or twice.
- Do not overuse it — you lose the colocation advantage of utility-first CSS.
Components vs @apply
In a React or Vue project, it is generally better to create a component:
// React component approach (preferred)
function Button({ variant = 'primary', children, ...props }) {
const styles = {
primary: 'bg-indigo-600 text-white hover:bg-indigo-700',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-600 text-white hover:bg-red-700',
};
return (
<button
className={`font-semibold py-2 px-4 rounded-lg ${styles[variant]}`}
{...props}
>
{children}
</button>
);
}
This approach keeps your styles in JavaScript, making them easier to maintain alongside your component logic.
Recap
@applyextracts repetitive utility patterns into CSS classes.@layerorganizes custom styles into base, components, and utilities.- Use
@layer componentsfor reusable classes so utilities can still override them. - In component frameworks, prefer creating components over
@apply. - Use
@applyfor elements that repeat heavily across templates.
Next, we will learn about CSS Grid utilities in Tailwind.