LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2817 min read

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.

What You Will Learn
  • 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

LayerPurposeExample
baseElement defaults and resets — things you would normally put in a global stylesheet.Styling every <h1>, setting default body font.
componentsReusable, named class patterns — multi-property styles you apply by class name..btn-primary, .card, .nav-link
utilitiesSmall, 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.

Mental Model

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

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

Next Lesson →

Tailwind with React/Next.js