The button is the component everyone builds first and almost nobody finishes. The finishing is not the hover animation. It is the six states, the loading case that must not resize the layout, the disabled case that has to stay explainable, and the decision about which of the four buttons on the screen is allowed to look like the important one.
449 public components on 21st have "button" in the name, which makes it the largest single category in the catalogue. Most of that volume is one axis: how it looks when the cursor arrives. The parts that decide whether a button works are elsewhere.
The hierarchy, which is the only part that is not taste
One primary action per view. Everything else is secondary, tertiary or a link. That is not a style rule, it is what makes a screen readable at a glance: if three buttons are filled and coloured, the reader has to read all three to find out which one the page wants.
The practical set is four levels. Primary, filled, one per view. Secondary, outlined, for the alternative that is still a real choice. Ghost or tertiary, for the actions that live in a toolbar or a row. And destructive, which is a colour treatment on top of any of those, never a fifth level.
Everything below is decoration applied to level one, and that is fine as long as it stays applied to level one.
The animated primary
This is where the catalogue's energy goes, and where a landing page gets its personality.
Interactive Hover Button by dillionverma is the restrained version, a hover transition that adapts between light and dark. Rainbow Button by the same author is the animated-gradient take, and Button Colorful with Hover Effect by kokonutd sits in the same family.
Liquid Glass Button by Ali Imam is the most-bookmarked button in the catalogue and rides the refractive-glass look, with Apple Tahoe Liquid Glass Button and Glass Button by Hossain Jahed as the neighbouring takes. Liquid Metal Button by johuniq, Neon Button by Gaz and Shiny Button by Ali Imam are the same idea in other materials.




The confirm-by-dragging one
Slide Button by reuno-ui is worth calling out separately, because it is not a style, it is a different interaction: the reader drags rather than taps.
It is the right control for one narrow case, an action that is destructive and irreversible, where the cost of an accidental tap is high. It is the wrong control everywhere else, because it is slower, it has no obvious keyboard equivalent, and a reader who has not seen one before has to work out what it wants. If you ship one, give it a plain button fallback for keyboard and screen reader users rather than assuming the gesture is available.
The states, which is where implementations fail
Six, and most components in every catalogue ship three.
Default, hover, active. The easy ones. One note: press feedback belongs on :active, and a transform is the cheapest honest signal. In Tailwind v4 that is active:scale-95 with a transition, not an animation library.
Focus-visible. A keyboard user has to see where they are. Removing the outline without replacing it is the single most common accessibility failure in a component library, and :focus-visible exists precisely so a mouse click does not draw a ring while a Tab press does.
Loading. The one that breaks layouts. If the label is replaced by a spinner, the button changes width and the row reflows. Reserve the label's width, or keep the text and put the indicator beside it. The button must also become unclickable in a way that survives a double click, which means disabling on submit rather than relying on the request being fast.
Disabled. A disabled button is invisible to a screen reader's tab order and explains nothing about why. Where the reason is fixable by the reader, prefer an enabled button that fails with a message. Where it genuinely cannot be used, aria-disabled with a tooltip beats disabled with silence.
The three that are not buttons
A link is not a button. If it navigates, it is an <a href>. That is what gives you middle-click, open in new tab, copy link address and a status bar preview, none of which a div with an onClick provides, and none of which a router push restores.
A submit is not a click. Inside a form, a real type="submit" gives you Enter-to-submit and native validation for free. Wiring onClick to a handler and leaving the form without a submit button breaks keyboard flow in a way nobody notices until someone complains.
An icon-only button needs a name. No text means no accessible name, so it needs aria-label and, for anyone using a mouse, a tooltip. A row of unlabelled icons is the most common unreadable toolbar on the web.




What a fancy button costs
Every effect in this article has a price, and three of them are worth knowing before the landing page ships.
A gradient that animates by moving background-position repaints the button on every frame. On one hero button that is nothing. On a grid of twenty cards each with a shiny border, it is a measurable slice of the frame budget on a mid-range phone.
A glass or blur effect uses backdrop-filter, which is the single most expensive common CSS property, because it forces the compositor to sample and blur whatever is behind it. A blurred button over an animated background is two expensive things stacked.
And every one of them is motion. Wrap the keyframes in @media (prefers-reduced-motion: reduce) and ship a static state, or you are overriding a system preference someone set deliberately.
Where else to look
The honest list of alternatives, because the answer is not always us:
| Source | Best for | Trade-off |
|---|---|---|
| shadcn/ui | The base: variants, sizes, asChild, focus and disabled handled | Deliberately plain, and the loading state is yours to add |
| Radix / Base UI | Behaviour without styling, when you own the design system | You write every pixel |
| Tailwind CSS | The state variants themselves: hover, focus-visible, active, disabled | Not components, just the language to write them in |
| 21st | The finished primaries: glass, gradient, neon, slide-to-confirm | Quality varies by author, so preview before you take it |
The split that survives contact with a real codebase: shadcn's Button as the base with your variants defined once, and a catalogue component only for the one hero action that is meant to look different from everything else.
Taking one
Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:
That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue. The 21st MCP covers the same ground without leaving the editor: ask Claude, Cursor or Codex for a button, get the previews inline, and let the agent write the file.
Frequently asked
- How many primary buttons should a screen have?
- One. Hierarchy is not a style rule, it is what makes a screen readable at a glance: if three buttons are filled and coloured, the reader has to read all three to find out which one the page wants. The workable set is primary filled, secondary outlined, ghost for toolbars, and destructive as a colour treatment on top of any of those rather than a fifth level.
- What states does a button component need?
- Six: default, hover, active, focus-visible, loading and disabled. Most libraries ship three. Focus-visible is the one that matters most, because removing the outline without replacing it is the commonest accessibility failure in component libraries. Loading is the one that breaks layouts, because swapping the label for a spinner changes the button's width and reflows the row.
- When should a link be a button?
- Never, if it navigates. An anchor with a real href gives you middle-click, open in a new tab, copy link address and a status-bar preview, none of which a div with an onClick provides and none of which a router push restores. Inside a form, use type="submit" so Enter submits and native validation runs.
- Do animated buttons hurt performance?
- One hero button, no. A grid of twenty animated ones, yes. A gradient that animates by moving background-position repaints on every frame, and glass effects use backdrop-filter, the most expensive common CSS property, because the compositor has to sample and blur what is behind it. Keep the effect on the single action that is meant to look different, and wrap the keyframes in a prefers-reduced-motion query.