Skip to content
Navigation

Button

Triggers an action with a side effect. Pure navigation is a link (`<a>`, router link) — even when it should look like a button.

Added 14 October 2026

Decide by side effect, not by looks:

  • The click has a side effect (submit, persist, mutate state) — even if it also navigates afterwards → Button.
  • The click only navigates → a real link, styled with buttonVariants(...) when it must look like a button. Middle-click, open-in-new-tab and screen readers depend on this. [a11y]
  • Carve-out: an icon-only “back” control in a flow may be a ghost Button for hit-target reasons; aria-label still mandatory.

Variants — which one and when

Naming warning: default means primary, not “ordinary button” — the name is inherited from shadcn and we keep it for ecosystem compatibility. An omitted variant also renders primary. So never reach for a bare <Button> as a neutral action: a neutral button is secondary or outline, and default must pass the one-per-surface rule below.

  • default — the one primary action of the current surface: a card, dialog, form or self-contained section — not the whole page. At most one visible per surface; a grid of cards legitimately shows one per card. See docs/patterns/action-hierarchy.md.
  • secondary — a stand-alone neutral action: card-body actions (“Continue editing”), toolbar controls, the “Back” companion next to a primary in a form row.
  • outline — a utility action attached to a context: reset-to-default, inline-edit save/cancel icon pairs, remediation buttons inside notices; also the demoted second action when the same card already shows a default.
  • ghost — lowest emphasis: dismiss/close/back, toolbar icon actions, repeated row actions in dense lists.
  • destructive — removing or revoking something; item-scoped (“remove this file”, “remove this site”). Confirmation is required only when the action is irreversible or costly — see docs/patterns/action-hierarchy.md.
  • floating — a control laid over a map (zoom, locate, fit all, expand): popover surface, no edge, its own shadow. Never on a regular page surface — there it is a secondary or outline. Stacks go in a ButtonGroup, which takes over the shadow and draws the dividers. See docs/patterns/map-controls.md.
  • link — an action that must read as inline text. Not for navigation — that is a real link. Currently unused in consumers; prefer the variants above.

Sizes — which one and when

  • default — forms, dialogs, most surfaces. Use it unless a rule below applies.
  • sm — companion actions inside cards and notices.
  • xs — extra-dense surfaces (tags, tight rows).
  • lg — prominent full-width section actions.
  • cta — the flow-defining forward action of the whole journey: advance the wizard, create the first campaign, pay. At most one visible per screen, always with variant="default", full-width when the container is a narrow stacked column. Never rebuild it with custom classes — use the size.
  • icon, icon-lg, icon-sm, icon-xs — icon-only; aria-label is mandatory [a11y]. Context guide from real usage: icon for toolbars and map/media controls, icon-sm for inline edit and dialog close, icon-xs for in-card dismiss.

States

  • Disabled — never the only explanation. Show why: a tooltip listing the blockers (the wizard-continue pattern) or an adjacent hint. Gate with “disabled with reason”; don’t hide the control. [research]
  • Busy — while an async action runs: disable, swap the label to a progressive form (“Sending…”, “Processing…”), add a spinner when the wait is noticeable.

Never

  • Two default buttons visible at once on the same surface.
  • size="cta" more than once per screen, with a variant other than default, or hand-faked via className.
  • An icon-only button without aria-label. [a11y]
  • A disabled button with no visible reason why.
  • A Button whose click handler only navigates — use a link.
  • docs/patterns/action-hierarchy.md — arranging multiple actions on one surface.
  • docs/patterns/copy.md — what the label says: sentence case, verb + object.
  • docs/patterns/forms.md — submit placement, validation, busy states.