Vision UI

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's render prop (element or render function). Styling comes from buttonVariants (CVA) in button.styles.ts. Plays a click sound on onMouseUp unless isSoundDisabled is set.
  • Button.Label: Default label chrome (text-sm font-medium text-white/90). String children on the root are wrapped in Button.Label automatically; use the explicit part for icons-plus-text layouts or custom render targets.
  • 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.).

Prop

Type

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.

Prop

Type

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.

On this page