Skip to main content

Search rules

Search all UI Guides rules by name, category, or source

layoutTailwind

@apply Is Not How You Share Styles

NEVER

NEVER: Collapse repeated markup into a `.btn { @apply … }` class to make it look "cleaner" — it reintroduces exactly what utilities removed: you invent a name, you jump between files to read the button's appearance, and one edit silently repaints every button. It also collapses at the first variant (`.btn-primary`, `.btn-sm`, disabled), where you start hand-rolling the variant system you already have. Extract a COMPONENT instead — a React component with CVA — which encapsulates behaviour too, not just the class string. `@apply` is legitimate only for markup you do not control (a `.prose` subtree, a third-party widget).

Extract a component when markup repeats, instead of collapsing utilities into a .btn class with @apply

/* no */ .btn { @apply inline-flex rounded-md px-4 py-2 …; }
// yes: const button = cva("inline-flex rounded-md", { variants: { size: { sm: "px-2 py-1", md: "px-4 py-2" } } });

Bad

Good

Why it matters

The instinct is understandable: a long `class="inline-flex items-center rounded-md px-4 py-2 …"` looks like duplication, and `.btn { @apply … }` makes it go away. But the duplication was never the problem utility classes had — it was the naming, the indirection, and the coupling. `.btn` brings all three back. You now maintain a name that means nothing to the browser, you cannot read the button's appearance from the button's markup, and any change to `.btn` silently repaints every button in the product, which is exactly the cascade problem Tailwind exists to remove.

It also collapses the moment you need a variant: a `.btn-primary`, a `.btn-sm`, a disabled state, and you are hand-rolling the variant system Tailwind already ships. Tailwind's own guidance is a ladder, and `@apply` is not on it: if the repetition is in a list, use a loop; if it is a real UI element, extract a *component* — a React component, with CVA for the variants — because a component encapsulates the markup and the behaviour too, not just the class string.

`@apply` earns its place in one narrow case: styling markup you do not control (a `.prose` subtree, a third-party widget) or an unavoidable single element, where a component is not an option. Reach for it there, not to tidy up.

Built by Gleb Stroganov, design engineer at Evil Martians.

The rules come from other people's skills and guidelines — Vercel, Rauno Freiberg, @Ibelick, impeccable, Emil Kowalski, Tailwind, RAMS — each one credited on the Sources page. The work here is extraction and wiring: every rule is pulled into one corpus, given a good and a bad example you can operate, a MUST/SHOULD/NEVER rule an agent can paste, and a link back to where it came from.