Interactive control for primary actions, form submission, and inline triggers.
Description
Button is the primary interactive control for triggering an action. It renders as a native <button> by default and exposes slots for leading and trailing icons, a loading state with a built-in spinner, and an asChild prop for rendering as a link or any custom element via Radix Slot.
Reach for it whenever a user performs an action: submitting a form, opening a dialog, dismissing a toast, kicking off an async operation, or navigating between pages. The variant axis maps to product intents (primary for the main action, neutral-solid / neutral-light for secondary, danger for destructive, success for confirmation, inverse pair for dark surfaces). The appearance axis controls emphasis from solid down to ghost.
Don't use Button for non-action elements. If the trigger sits inside flowing prose and reads like part of the sentence, use TextLink. If a row of related actions belongs in a single visual cluster, use ButtonGroup. If it's a dedicated dismiss affordance on a dialog or toast, use CloseButton. If it carries metadata rather than triggering an action, use Badge.
Installation
Usage
Examples
Variants
variant sets the semantic color intent. Seven options cover the main action (primary), neutral surfaces (neutral-solid, neutral-light), destructive (danger), confirm (success), and the inverse pair for dark backgrounds (inverse-solid, inverse-light).
Appearance
appearance controls fill weight. solid (default) is high-contrast for the main action, outline adds a border with no fill, soft uses a tinted background, and ghost drops the surface entirely until hover.
Sizes
Five sizes scale from xs (20px tall) to xl (48px). lg is the default and pairs with body type.
Shape
shape controls the radius. rounded (default) follows the size scale, pill is fully rounded, square strips the radius for tabular or chrome layouts.
With Icon
Use leadingIcon and trailingIcon to attach an icon to either side of the label. The component sizes the icon to match the active size and handles the gap.
Icon Only
iconOnly switches to a square footprint with no horizontal padding. Always pair it with an aria-label, since the icon alone is not announced by screen readers.
Loading
loading replaces the leading icon with a spinner, sets aria-busy, and blocks pointer events so the action cannot fire twice. The spinner color is picked to match the active variant and appearance.
As Child
asChild renders the Button styles around any child element via Radix Slot. Most commonly used to slot in an <a> (or framework Link) so a button-styled link still navigates.
Accessibility
Button is a native <button> by default and inherits its implicit button role, focus ring, and activation keys. No manual ARIA wiring is needed for the default case.
ARIA notes:
loadingsetsaria-busy="true"on the root and disables pointer interaction; pair it with a visible label oraria-labelso the busy state is announced.iconOnlyrequires anaria-labelbecause the icon alone has no accessible name.- When
asChildis combined withdisabled, the rendered element is not a<button>and cannot use the nativedisabledattribute. The component switches toaria-disabled="true"instead. If you need the element fully inert, also remove it from the tab order (tabIndex={-1}).
Styling
Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):
Data slots and attributes: the component sets these for CSS targeting:
data-slot="button"on the root element.data-slot="button-icon"on each icon wrapper (leading icon, trailing icon, and the loading spinner in label mode).data-slot="button-label"on the inner text wrapper.data-variant="<variant>",data-appearance="<appearance>",data-size="<size>"on the root.aria-busy="true"on the root whenloading.aria-disabled="true"on the root whenasChildis combined withdisabled.
Target a specific state in CSS:
Related Components
- Input Group: wraps Buttons with text inputs, selects, and addons inside a single input shell.
- Input: the text input primitive that Buttons most often submit or filter.
- Input Stepper: numeric input that exposes Button-styled increment and decrement triggers.
- Inline Alert: inline status block that often pairs with a Button as its dismiss or follow-up action.
API Reference
Button
Interactive control for triggering actions. Extends React.ComponentProps<"button">, so any standard button attribute (type, id, aria-*, onClick, etc.) is accepted.