@apply Directive
What @apply is for
The @apply directive lets you compose Tailwind utilities into a single CSS rule. In production, it is best used for stable, low-level primitives and for reducing duplication when a pattern is repeated broadly. It is not a replacement for components, and it should not become a second styling system that bypasses your token strategy.
When @apply makes sense
- Stable primitives: card shells, input base styles, button base styles.
- Consistency enforcement: ensuring every instance shares identical focus states, borders, spacing, and typography.
- Framework integration points: styling third-party markup where editing templates is painful.
When @apply is a bad idea
- One-off styling: if the pattern appears once, keep utilities inline.
- Highly dynamic states: many variants and states often belong in a component layer.
- Rebuilding semantic CSS: creating dozens of classes to avoid writing utilities defeats Tailwind’s clarity.
Basic example
@layer components {
.ui-card {
@apply rounded-lg border border-slate-200 bg-white shadow-sm;
}
.ui-card-header {
@apply px-5 pt-5 pb-3 border-b border-slate-100;
}
.ui-card-body {
@apply px-5 py-4;
}
}
Use layers correctly
Always place @apply rules inside the correct layer. This ensures predictable ordering and avoids specificity fights.
- @layer base: element defaults and global resets.
- @layer components: stable component primitives.
- @layer utilities: custom utilities that behave like Tailwind utilities.
Keep specificity low
Prefer single-class selectors like .ui-button instead of nested selectors. Tailwind’s utility approach works best when specificity remains low and predictable.
Variant patterns without overgrowth
If you need variants, keep the API tight. Two or three variants often cover most use cases. If variants multiply, move the logic to a component layer.
@layer components {
.ui-btn {
@apply inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition;
@apply focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.ui-btn-primary {
@apply bg-brand-600 text-white hover:bg-brand-700 focus:ring-brand-500;
}
.ui-btn-ghost {
@apply bg-transparent text-slate-900 hover:bg-slate-100 focus:ring-slate-300;
}
}
Tokens first, then @apply
@apply should consume your design tokens, not replace them. If you are using semantic color names and tokenized scales, keep your component rules aligned with those tokens. This preserves long-term flexibility.
Performance considerations
@apply does not automatically bloat CSS, but uncontrolled class extraction can produce large component layers. Keep your component set small and audit output size regularly.
Production checklist
- Is this pattern repeated enough to justify extraction?
- Is it stable and unlikely to change frequently?
- Is the selector simple and low-specificity?
- Are tokens and focus states consistent across the system?
- Would a template component be clearer than CSS extraction?