Button
Interactive button with variants and sizes, optional label styling, and a group wrapper for pill-style toolbars.
Import
import { Button, buttonVariants } from '@/components/button'Anatomy
The module exposes one namespace object. Button is callable as the root control (same element as Button.Root). Use Button.Label for text inside a button (applied automatically for string children). Use Button.Group to wrap sibling buttons with shared spacing and rounded chrome.
<Button.Group>
<Button variant="secondary">Back</Button>
<Button variant="primary">Continue</Button>
</Button.Group>Equivalently, name the root explicitly:
<Button.Group>
<Button.Root variant="secondary">Back</Button.Root>
<Button.Root variant="primary">Continue</Button.Root>
</Button.Group>- Button / Button.Root: Renders a
<button>by default, or composes with another element through Base UI'srenderprop (element or render function). Styling comes frombuttonVariants(CVA) inbutton.styles.ts. Plays a click sound ononMouseUpunlessisSoundDisabledis set. - Button.Label: Default label chrome (
text-sm font-medium text-white/90). String children on the root are wrapped inButton.Labelautomatically; use the explicit part for icons-plus-text layouts or customrendertargets. - Button.Group: Layout wrapper with horizontal gap and styles so nested buttons read as a segmented / pill group.
Usage
Variants and sizes
variant controls foreground, overlay, and icon treatment (default, primary, secondary, destructive, selected, link). size is default or icon (square hit target, circular --radius).
<Button variant="primary" size="default">
Save
</Button>
<Button variant="secondary" size="icon" aria-label="Settings">
<GearIcon />
</Button>Labels
Plain string children are wrapped in Button.Label so typography stays consistent. For mixed content (icon + text), compose Button.Label explicitly.
<Button variant="primary">
<PlusIcon data-slot="icon" />
<Button.Label>New</Button.Label>
</Button>Override the label element or classes when needed:
<Button variant="link">
<Button.Label className="text-xs uppercase tracking-wide">
Learn more
</Button.Label>
</Button>Render as another element
Use the root render prop when the interactive surface should be another component, such as a router Link.
import { Link } from '@tanstack/react-router'
<Button render={<Link to="/docs" />} variant="link">
Read docs
</Button>Rendered components must forward their ref and spread received props onto the DOM element that should receive button styles and events.
Click sound
By default the root plays a grid-select sound on onMouseUp. Pass isSoundDisabled to silence it (for example when PressableFeedback already owns the interaction).
<Button isSoundDisabled variant="secondary">
Quiet action
</Button>Reusing styles without the component
Use buttonVariants when you need the same classes on a non-button element (custom primitives, links, or tests).
import { buttonVariants } from '@/components/button'
<a className={buttonVariants({ variant: 'link', className: 'inline-flex' })}>
Styled like a link button
</a>Example
import { Button } from '@/components/button'
export default function ButtonExample() {
return (
<Button.Group className="max-w-md">
<Button variant="secondary">Cancel</Button>
<Button variant="primary">Confirm</Button>
</Button.Group>
)
}API Reference
Button (Button.Root)
The callable Button component is Button.Root. Beyond the table below, the root accepts normal <button> HTML attributes from React (onClick, disabled, aria-*, className, etc.).
variant and size mirror buttonVariants in button.styles.ts (including defaultVariants when omitted).
Button.Label
Label text inside a button. Beyond the table below, rendered output is a <span> by default and accepts the props Base UI forwards through render.
Button.Group
Button.Group is a plain div wrapper: use standard React.HTMLAttributes<HTMLDivElement> (for example className, style, role, event handlers). There are no component-specific props beyond that.
ButtonProps
Type alias for ButtonRootProps — use either name when typing the root component.