Skip to content

Button

Triggers an action or event, such as submitting a form or opening a dialog.

Actionv0.1.0experimentalWCAG 2.2 AAView spec
import { Button } from "@rdloom/react";

export default function ButtonVariantsExample() {
  return (
    <div className="flex flex-wrap gap-3">
      <Button>Save</Button>
      <Button variant="secondary">Cancel</Button>
      <Button variant="ghost">Skip</Button>
      <Button variant="danger">Delete</Button>
    </div>
  );
}

Installation

npx rdloom add button

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/button.json

Works, but without upgrade tracking.

Usage

import { Button } from "@rdloom/react";

<div className="flex flex-wrap gap-3">
  <Button>Save</Button>
  <Button variant="secondary">Cancel</Button>
  <Button variant="ghost">Skip</Button>
  <Button variant="danger">Delete</Button>
</div>

Sizes

import { Button } from "@rdloom/react";

export default function ButtonSizesExample() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
    </div>
  );
}

Loading

import { useState } from "react";
import { Button } from "@rdloom/react";

export default function ButtonLoadingExample() {
  const [saving, setSaving] = useState(false);
  return (
    <Button
      isLoading={saving}
      onPress={() => {
        setSaving(true);
        setTimeout(() => setSaving(false), 2000);
      }}
    >
      {saving ? "Saving" : "Save changes"}
    </Button>
  );
}

Disabled

import { Button } from "@rdloom/react";

export default function ButtonDisabledExample() {
  return (
    <div className="flex flex-wrap gap-3">
      <Button isDisabled>Publish</Button>
      <Button variant="secondary" isDisabled>
        Archive
      </Button>
    </div>
  );
}

With icon

import { Button } from "@rdloom/react";

function PlusIcon() {
  return (
    <svg aria-hidden="true" viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M8 3v10M3 8h10" />
    </svg>
  );
}

export default function ButtonWithIconExample() {
  return (
    <div className="flex flex-wrap gap-3">
      <Button>
        <PlusIcon /> New project
      </Button>
      {/* Icon-only buttons need a label for screen readers. */}
      <Button variant="ghost" aria-label="Add item">
        <PlusIcon />
      </Button>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
variant

Visual emphasis of the button.

"primary" | "secondary" | "ghost" | "danger""primary"
size

Height, padding and font size.

"sm" | "md" | "lg""md"
isDisabled

Prevents interaction and dims the button.

booleanfalse
isLoading

Shows a spinner and blocks presses. The button stays focusable, is marked aria-disabled, and the pending state is announced.

booleanfalse
childrenrequired

Button label. Icon-only buttons must pass aria-label instead.

nodenone
onPress

Called when the button is pressed by mouse, touch or keyboard.

() => voidnone

Accessibility

Role button, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Enter activates
  • Space activates

Screen readers announce

  • Announced as a button with its visible label
  • An icon-only button (the ⚙ in the playground) announces its aria-label, not the icon
  • Disabled: announced as unavailable or dimmed
  • Loading: announced as unavailable, and the busy state is spoken when loading starts

What your code must do

  • Must have visible text or an aria-label
  • Focus ring must meet 3:1 contrast against its background

Guidelines

Use it when

  • Performing an action on the current page
  • Submitting a form

Avoid it when

  • Navigating to another page: use Link

Don't

  • More than one primary button in the same area
  • Vague labels like 'Click here'

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-action-primary
  • --rd-color-action-primary-hover
  • --rd-color-action-on-primary
  • --rd-color-action-danger
  • --rd-color-surface-subtle
  • --rd-color-border-default
  • --rd-color-text-default
  • --rd-color-focus-ring
  • --rd-radius-control
  • --rd-space-control-x