Button

Displays a button or a component that looks like a button, with multiple style variants and sizes

Installation

pnpm add @wandercom/design-system-web

Usage

import { Button } from '@wandercom/design-system-web/ui/button';

export function Example() {
  return <Button>Click me</Button>;
}

Composition

Compose with other components using asChild:

import { Button } from '@wandercom/design-system-web/ui/button';
import Link from 'next/link';

export function LinkButton() {
  return (
    <Button asChild>
      <Link href="/dashboard">Dashboard</Link>
    </Button>
  );
}

Variants

Primary

Loading example...
<Button variant="primary">Primary</Button>

Secondary

Loading example...
<Button variant="secondary">Secondary</Button>

Outline

Loading example...
<Button variant="outline">Outline</Button>

Ghost

Loading example...
<Button variant="ghost">Ghost</Button>

Destructive

Loading example...
<Button variant="destructive">Destructive</Button>

Checkout

Loading example...
<Button variant="checkout">Checkout</Button>
Loading example...
<Button variant="link">Link</Button>

Loading

Set loading to show an animated spinner and disable the button. For standard buttons the spinner appears inline before the label. For icon-only and asChild buttons, the spinner overlays the content.

Loading example...
<Button loading>Continue</Button>

Sizes

Four text sizes and four icon-only sizes are available. The default size is lg.

<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>

<Button size="icon-xs">
  <Icon />
</Button>
<Button size="icon-sm">
  <Icon />
</Button>
<Button size="icon-md">
  <Icon />
</Button>
<Button size="icon-lg">
  <Icon />
</Button>

Props

variant?

'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'checkout' | 'link' | 'unstyled'
Visual style variant. Defaults to 'primary'.

size?

'xs' | 'sm' | 'md' | 'lg' | 'icon-xs' | 'icon-sm' | 'icon-md' | 'icon-lg'
Size variant. Defaults to 'lg'.

loading?

boolean
When true, shows an animated spinner and disables the button. Defaults to false.

asChild?

boolean
Renders the child element and merges props instead of rendering a native button. Uses Radix UI Slot. Defaults to false.

disabled?

boolean
Disables the button when true. Also set automatically when loading is true.

className?

string
Additional CSS classes.

Accessibility

The Button renders a native <button> element by default, inheriting built-in keyboard and screen reader support. When using asChild, ensure the child element is focusable and has the correct role.

Keyboard interaction:

  • Space - Activates the button
  • Enter - Activates the button

When loading is true the button is automatically disabled, preventing duplicate submissions. The spinner is marked with aria-hidden when not active.

For icon-only buttons, always provide an accessible label via aria-label or visually hidden text so screen readers can announce the button's purpose.

Button