Reusable Components with @layer
Understand @layer components vs @layer utilities, and learn how to organize custom CSS properly alongside Tailwind's generated styles.
Introduction
When you write plain CSS alongside @apply-based classes, ordinary CSS cascade rules apply — the last rule declared usually wins, regardless of how specific its intent was. @layer solves this by telling Tailwind which conceptual bucket a block of custom CSS belongs to, so it gets merged into the build in a predictable, sensible order instead of wherever it happens to sit in the file.
- Why @layer exists and what problem it solves.
- The purpose of the base, components, and utilities layers.
- How to organize custom CSS so it always loses or wins overrides predictably.
Why @layer Exists
Tailwind's own output is already organized into three internal layers via the @tailwind base;, @tailwind components;, and @tailwind utilities; directives you added back in the setup lesson. Without @layer, any custom CSS you write yourself sits outside that system, and its position in the final stylesheet depends purely on where you typed it in the source file — which makes override behavior fragile and hard to reason about.
/* styles/input.css */@tailwind base;@tailwind components;@tailwind utilities;
/* Without @layer, this rule's cascade position depends on file order, not on what it conceptually is */.btn-primary { background-color: #2563eb;}The Three Layers
| Layer | Purpose | Example |
|---|---|---|
| base | Element defaults and resets — things you would normally put in a global stylesheet. | Styling every <h1>, setting default body font. |
| components | Reusable, named class patterns — multi-property styles you apply by class name. | .btn-primary, .card, .nav-link |
| utilities | Small, single-purpose custom utilities meant to be combined freely, just like Tailwind's own. | .text-shadow, .scrollbar-hide |
@layer base
Use base for element-level defaults that should apply globally without needing a class — the same role a traditional CSS reset plays.
@layer base { h1 { @apply text-3xl font-bold text-gray-900; } h2 { @apply text-2xl font-semibold text-gray-900; } a { @apply text-blue-600 underline-offset-2 hover:underline; }}@layer components
This is where classes built with @apply usually belong — reusable, named patterns like buttons, cards, and badges that bundle several properties together under one class.
@layer components { .btn-primary { @apply px-4 py-2 rounded-md bg-blue-600 text-white font-semibold hover:bg-blue-700 transition-colors; } .card { @apply p-6 bg-white rounded-lg shadow-sm border border-gray-200; }}Because these live in the components layer, a plain utility class placed after .btn-primary in your markup — like bg-green-600 — still overrides its background color, exactly the way you would expect from Tailwind's own utility-over-component precedence.
<button class="btn-primary bg-green-600">Confirm</button><!-- Renders green, because utilities always beat components regardless of source order -->@layer utilities
Use utilities for small, single-purpose custom classes you want to behave exactly like Tailwind's own — freely combinable, and able to override component styles.
@layer utilities { .text-shadow { text-shadow: 0 2px 4px rgb(0 0 0 / 0.2); } .scrollbar-hide { scrollbar-width: none; } .scrollbar-hide::-webkit-scrollbar { display: none; }}<h1 class="text-4xl font-bold text-shadow">Bold Heading</h1>Layer Order and Overrides
The final CSS is emitted in a fixed order — base, then components, then utilities — no matter where in your source file each @layer block is written. This is exactly why a utility class can always override a component class: utilities are emitted last, and later CSS wins when specificity is equal.
Think of the three layers as three separate buckets Tailwind fills in order. Where you physically type @layer components in your file does not matter — Tailwind moves its contents into the components bucket regardless, keeping cascade order predictable.
Common Mistakes
- Writing custom CSS without any @layer wrapper, leaving its cascade position dependent on fragile file ordering.
- Putting a reusable class like .btn-primary in @layer utilities, where a Tailwind utility class might unexpectedly override parts of it.
- Putting a single-purpose helper like .text-shadow in @layer components, where it may lose to ordinary utility classes it was meant to combine with.
- Assuming @layer purges unused custom classes on its own — content scanning (covered in the "Performance & Purging" lesson) is what actually removes unused CSS.
Best Practices
- Use base only for unclassed element defaults, never for anything that needs a class to activate.
- Put multi-property, named patterns in components — this is the natural home for @apply-based classes.
- Put small, single-purpose, freely-combinable helpers in utilities so they interact with Tailwind's own utilities exactly as expected.
- When unsure which layer fits, ask whether the class behaves more like a component (a fixed bundle of styles) or a utility (one small, composable effect).
Frequently Asked Questions
No, @apply works outside of @layer too, but without it your custom class's position in the cascade depends on file order rather than Tailwind's predictable base/components/utilities sequence.
Yes, Tailwind supports registering additional named layers via the addComponents/addUtilities plugin API or custom @layer names inserted via a plugin, though base, components, and utilities cover the vast majority of use cases.
Not directly — purging is driven by whether a class name appears in your content-scanned files, not by which layer it lives in. Classes in any layer are removed if they are never used in your markup.
Key Takeaways
- @layer tells Tailwind which conceptual bucket a block of custom CSS belongs to: base, components, or utilities.
- Output order is always base → components → utilities, regardless of source file order.
- Utility classes always override component classes at equal specificity, which is why .btn-primary bg-green-600 renders green.
- Choosing the right layer keeps custom CSS behaving predictably alongside Tailwind's own generated styles.
Summary
You learned how @layer organizes custom CSS into base, components, and utilities so that cascade and override behavior stays predictable as a project grows. Next, you will bring everything together by setting up Tailwind inside a real React and Next.js project.