Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Custom utilities and components

Sometimes the ready classes are not enough: you need a CSS property that has no utility, or a repeated set of styles for HTML you do not control. Tailwind 4 gives you three tools: @utility — your own utility, @layer components — a component class, and @apply — utilities inserted into an ordinary CSS rule.

@utility

@utility creates a new utility. It behaves like a built-in one: works with hover:, md: and other variants and is sorted correctly in the CSS. It goes into the main CSS file, for example src/style.css:

@import "tailwindcss";
 
@utility scrollbar-hidden {
  scrollbar-width: none;
}

With the browser script it goes inside <style type="text/tailwindcss">:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
  @utility scrollbar-hidden {
    scrollbar-width: none;
  }
</style>
 
<div class="scrollbar-hidden flex w-64 gap-2 overflow-x-auto">
  <span class="shrink-0 rounded-full bg-gray-100 px-3 py-1">Headphones</span>
  <span class="shrink-0 rounded-full bg-gray-100 px-3 py-1">Speakers</span>
  <span class="shrink-0 rounded-full bg-gray-100 px-3 py-1">Chargers</span>
  <span class="shrink-0 rounded-full bg-gray-100 px-3 py-1">Cables</span>
</div>

The row of categories scrolls sideways, but the scrollbar is hidden.

Components and @apply

@apply inserts the styles of utilities into an ordinary CSS rule. In the components layer this is used for component classes that utilities can still override:

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
  @layer components {
    .btn-primary {
      @apply rounded-lg bg-indigo-600 px-4 py-2 font-medium text-white hover:bg-indigo-500;
    }
  }
</style>
 
<button class="btn-primary">Add to cart</button>
<button class="btn-primary bg-gray-900">Buy now</button>

Both buttons are styled with one class. On the second, the bg-gray-900 utility overrides the component's background: Tailwind's utilities layer beats the components layer.

When @apply fits

SituationWhat to use
A piece of markup repeats in templatesA template component (lesson in the Practice chapter)
HTML is generated by other code: a CMS, Markdown, a widget@apply or @layer components
You need a new CSS property as a class@utility

@apply is convenient, but if you move your whole layout into it you are back to plain CSS with invented names — and you lose Tailwind's main advantage. First look for a way to reuse the markup, not the classes.

@apply in component files

In the <style> block of a Vue or Svelte component Tailwind does not know your theme. To make @apply work there, the main CSS file is referenced at the top of the block with @reference "../style.css"; — it gives access to the theme without duplicating the styles.

Common mistake

Making an @apply class for every element:

.card-title {
  @apply text-lg font-semibold text-gray-900;
}
 
.card-price {
  @apply text-xl font-bold;
}

This is the same CSS with invented names, only longer: to change the heading you now have to find a rule in another file. Keep utilities in the markup and use @apply only for the cases in the table above.

Thanks for reading!

Donate to the site — we work hard for you.