v0.5

Button

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

pnpm dlx @create-ui/cli add button

Usage

import { Button } from "@/components/ui/button"
<Button>Save</Button>

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.

KeyDescription
SpaceActivates the button.
EnterActivates the button.
TabMoves focus to the next element.

ARIA notes:

  • loading sets aria-busy="true" on the root and disables pointer interaction; pair it with a visible label or aria-label so the busy state is announced.
  • iconOnly requires an aria-label because the icon alone has no accessible name.
  • When asChild is combined with disabled, the rendered element is not a <button> and cannot use the native disabled attribute. The component switches to aria-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()):

<Button className="tracking-wide uppercase">Save</Button>

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 when loading. aria-disabled="true" on the root when asChild is combined with disabled.

Target a specific state in CSS:

[data-slot="button"][data-variant="primary"][data-appearance="solid"] {
  /* … */
}
  • 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.

Props

PropTypeDefaultDescription
asChildbooleanfalseRender as the child element via Radix Slot. Use to slot the Button styles onto an <a> or framework link.
loadingbooleanfalseShow the loading spinner, set aria-busy, and block pointer interaction. Replaces leadingIcon when on.
leadingIconReact.ReactNode-Icon rendered before the label. Wrapped in a data-slot="button-icon" span. Ignored when iconOnly.
trailingIconReact.ReactNode-Icon rendered after the label. Wrapped in a data-slot="button-icon" span. Ignored when iconOnly.
iconOnlybooleanfalseSquare footprint with no horizontal padding; renders children as the only content. Pair with aria-label.
disabledbooleanfalseNative disabled on the <button>. Switches to aria-disabled when combined with asChild.
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-Button label, or the icon when iconOnly. With asChild, the single child element receives the styles.

Variants

VariantOptionsDefaultDescription
variant"primary" "neutral-solid" "neutral-light" "danger" "success" "inverse-solid" "inverse-light""primary"Semantic color intent. Inverse options target dark surfaces.
appearance"solid" "outline" "soft" "ghost""solid"Fill weight. solid for emphasis, outline for a quiet border, soft tinted, ghost transparent.
size"xs" "sm" "md" "lg" "xl""lg"Size scale; controls height, padding, type, and icon size.
shape"rounded" "pill" "square""rounded"rounded follows the size scale, pill is fully rounded, square strips the radius.