Skip to content

ComponentsButtons

Button

Triggers an action or event. Six visual styles, seven colors, eight sizes and a built-in pending state — with press handling that works the same on mouse, touch, pen and keyboard.

React AriaSource

Installation

pnpm dlx shadcn@latest add @desyne/button

The CLI installs dependencies and any other components this one uses.

Usage

tsx
import { Button } from "@/components/ui/button";
tsx
<Button onPress={() => save()}>Save changes</Button>

Use onPress, not onClick

React Aria normalizes mouse, touch, pen and keyboard input into a single onPress event. It fires once per press, ignores scroll gestures on touch devices and never leaves a "sticky" hover state behind. onClick still works, but you lose those guarantees.

When to use

  • Use a button to perform an action: submit a form, open a dialog, start a process.
  • Use a link to navigate. If it changes the URL, it should be a link — style it with buttonVariants if it needs to look like a button.
  • Use a toggle button for an on/off state that persists, like bold or mute.
  • Keep one solid button per view for the primary action; use outline, soft or ghost for everything else.

Anatomy

A button is a single element. Icons, spinners and keyboard hints are plain children that the button sizes and spaces for you.

tsx
<Button>
  <Icon />        {/* optional leading icon, auto-sized */}
  Label
  <Kbd />         {/* optional trailing hint or icon */}
</Button>
PartRendersNotes
Button<button>Root. Carries data-slot="button" and all state attributes.
Icon<svg>Sized by the button (size-4, or size-3/size-3.5 for small sizes) unless you set a size-* class.
Spinner<svg>Injected before the label while isPending is true.

Examples

Variants

solid for the primary action, soft and outline for secondary actions, dashed for "add" affordances, ghost for toolbars and dense UIs, and link for inline actions.

Colors

Every variant works with every color. outline, dashed and ghost default to neutral; the rest default to primary. Use brand for accent actions, and danger only for destructive ones.

solidsoftoutlineghost
primary
brand
neutral
danger
success
warning
info

Sizes

md (32px) is the default. xs and sm suit tables and toolbars, lg suits marketing pages and touch-first layouts.

Full width

Buttons are inline by default. Add w-full for stacked actions in cards, forms and mobile layouts.

With icon

Icons are sized and spaced automatically. Place them before the label for actions ("Add", "Upload") and after it for direction ("Next", external links).

Icon only

Use the icon-xs, icon-sm, icon and icon-lg sizes for square buttons. They have no visible text, so aria-label is required.

With tooltip

Pair icon-only buttons with a Tooltip so sighted mouse users get the same label screen readers announce.

With keyboard shortcut

Put a Kbd inside the button to advertise its shortcut. The button doesn't bind the key for you — register it with your own hotkey handler.

Button group

Wrap related buttons in a React Aria Group and remove the inner radii to attach them. The group gives assistive tech a single labelled region.

Pending

isPending shows a spinner, blocks further presses and keeps focus on the button, so keyboard and screen-reader users aren't thrown back to the top of the page. The button also announces its busy state.

Async actions

Drive isPending from your own state to show progress and a success state for async work.

Disabled

isDisabled removes the button from the tab order and ignores presses. Prefer isPending while work is in flight, and consider explaining why an action is unavailable instead of silently disabling it.

In a form

Buttons default to type="button". Set type="submit" or type="reset" to take part in a form; native validation and FormData work as usual.

Links must stay links for navigation to work (middle-click, "open in new tab", prefetching). Apply buttonVariants to a React Aria Link, Next.js Link or any element to borrow the button styles.

tsx
import NextLink from "next/link";
import { buttonVariants } from "@/components/ui/button";

<NextLink href="/pricing" className={buttonVariants({ variant: "outline" })}>
  See pricing
</NextLink>;

Recipes

Toolbar

Ghost icon buttons inside a React Aria Toolbar get arrow-key navigation between items, with tooltips that show the shortcut.

Split button

A primary action attached to a Menu of alternatives.

Empty state

A primary and a secondary action, centered in an empty-state card.

No projects yet

Create your first project or import an existing one.

Accessibility

  • Renders a native <button>, so it's focusable, announced as a button and works with every assistive technology out of the box.
  • The focus ring only appears for keyboard focus (data-focus-visible), never on mouse click.
  • Icon-only buttons must have an aria-label (or aria-labelledby).
  • isPending sets aria-disabled and announces the busy state, but keeps the button in the tab order so focus isn't lost.
  • isDisabled sets the native disabled attribute and removes the button from the tab order.
  • Don't nest interactive elements (links, other buttons) inside a button.

Keyboard

KeyAction
TabMoves focus to the button
Space / EnterPresses the button

Styling

Data attributes

Style states with Tailwind's native data variants, e.g. data-pressed:scale-[0.98].

AttributePresent when
data-hoveredHovered with a mouse or pen (never on touch)
data-pressedBeing pressed
data-focusedFocused by any means
data-focus-visibleFocused with the keyboard
data-disabledisDisabled is true
data-pendingisPending is true
data-slot="button"Always — target buttons from a parent with *:data-[slot=button]:…

Tone variables

Colors are applied through two CSS variables, so you can create a one-off tone without touching the variants:

tsx
<Button className="[--tone:var(--color-pink-600)] [--tone-fg:white]">
  Custom tone
</Button>
VariableUsed for
--toneBackground (solid), text and border (other variants), focus ring
--tone-fgText on solid buttons

Render props

className and children also accept a function of the button's state:

tsx
<Button className={({ isPressed }) => (isPressed ? "scale-95" : "")}>
  {({ isPending }) => (isPending ? "Saving…" : "Save")}
</Button>

buttonVariants

The tailwind-variants function behind the component. Use it to style non-button elements, or to build your own variants on top.

tsx
import { buttonVariants } from "@/components/ui/button";

buttonVariants({ variant: "soft", color: "success", size: "sm" });

API Reference

Button

Prop

Type

Also accepts every prop of React Aria's Button, including focus, hover and keyboard handlers.

buttonVariants

Prop

Type

  • Toggle Button — a button with a persistent on/off state.
  • Toggle Button Group — single or multiple selection across a set of buttons.
  • Link — for navigation.
  • Menu — for a button that opens a list of actions.