# Button

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

## Installation

```tsx
import { Button } from "@makeplane/propel/components/button";
```
## Usage

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function BasicDemo() {
  return <Button label="Button" variant="primary" size="sm" stretch="auto" />;
}
```
## Examples

### With an icon

Pass `icon` and pick its side with `iconPosition` (`start` by default).

```tsx
import { Button } from "@makeplane/propel/components/button";
import { ArrowNarrowRightOutline, AddOutline } from "@makeplane/propel/icons";

export default function WithIconDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button label="New" variant="primary" size="sm" stretch="auto" icon={<AddOutline />} />
      <Button
        label="Continue"
        variant="secondary"
        size="sm"
        stretch="auto"
        icon={<ArrowNarrowRightOutline />}
        iconPosition="end"
      />
    </div>
  );
}
```

### Variants

Variant sets the visual weight and palette: `primary`, `secondary`, `tertiary`, and `ghost`, plus
`danger` and `danger-outline` for destructive actions.

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function VariantsDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button label="Primary" variant="primary" size="sm" stretch="auto" />
      <Button label="Secondary" variant="secondary" size="sm" stretch="auto" />
      <Button label="Tertiary" variant="tertiary" size="sm" stretch="auto" />
      <Button label="Ghost" variant="ghost" size="sm" stretch="auto" />
      <Button label="Danger" variant="danger" size="sm" stretch="auto" />
      <Button label="Danger outline" variant="danger-outline" size="sm" stretch="auto" />
    </div>
  );
}
```

### Sizes

`size` sets the height and everything that follows from it — text, glyph, padding, and radius
(xs 20 / sm 24 / md 28 / lg 32 px). There is no default: every call site picks one. `xs` is
inline chrome only: it ships a synthesized 24px pointer target, which is why the grouped families
have no `xs` rung at all — [ButtonGroup](/components/button-group) runs `sm`–`md` and
[SplitButton](/components/split-button) `sm`–`lg`, since their segments touch and adjacent targets
would intersect. See [Sizing](/foundations/sizing) for the full ladder.

```tsx
import { Button, type ButtonSize } from "@makeplane/propel/components/button";

const SIZES: ButtonSize[] = ["xs", "sm", "md", "lg"];

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {SIZES.map((size) => (
        // The label carries a word as well as the rung: with a two-character label every button
        // hits its min-width floor (internal/button-geometry.ts) and the padding ramp stops
        // being visible.
        <Button key={size} label={`Button ${size}`} variant="primary" size={size} stretch="auto" />
      ))}
    </div>
  );
}
```

### Loading

The `loading` state shows a spinner in the icon slot, sets `aria-busy`, and blocks interaction while staying focusable.

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function LoadingDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button label="Saving" variant="primary" size="sm" stretch="auto" loading />
      <Button label="Inviting member" variant="secondary" size="sm" stretch="auto" loading />
      <Button label="Please wait" variant="tertiary" size="sm" stretch="auto" loading />
    </div>
  );
}
```

### Full width

`stretch="full"` makes the button fill its container, e.g. a form row or a mobile CTA.

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function FullWidthDemo() {
  return (
    <div className="flex w-64 flex-col gap-2">
      <Button label="Full-width" variant="primary" size="sm" stretch="full" />
      <Button label="Full-width outline" variant="secondary" size="sm" stretch="full" />
    </div>
  );
}
```

### Disabled

`disabled` is the hard, non-focusable disabled state; unlike `loading`, it removes the button from the tab order.

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function DisabledDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button label="Save changes" variant="primary" size="sm" stretch="auto" disabled />
      <Button label="Delete workspace" variant="danger-outline" size="sm" stretch="auto" disabled />
    </div>
  );
}
```

### As a link

`render={<a href=… />}` swaps the underlying element and `nativeButton={false}` applies Base UI's non-native button semantics: the control is announced as a button while the `<a>` keeps native navigation.

```tsx
import { Button } from "@makeplane/propel/components/button";

export default function AsLinkDemo() {
  return (
    <Button
      label="Open reports"
      variant="secondary"
      size="sm"
      stretch="auto"
      nativeButton={false}
      render={<a href="#reports" />}
    />
  );
}
```
## API Reference

### Button

The ready-made `Button`: grafts Base UI's `Button` behavior onto the styled `Button` element and
lays out an optional `icon` beside the label (`iconPosition`), swapping it for the `loading`
spinner in the same slot. Content — the label, icon, and `loading` state — is not a variant.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` (required) | `"primary" \| "secondary" \| "tertiary" \| "ghost" \| "danger" \| "danger-outline"` | — |  |
| `size` (required) | `"xs" \| "sm" \| "md" \| "lg"` | — |  |
| `stretch` (required) | `"auto" \| "full"` | — |  |
| `label` (required) | `string` | — | Visible button label. |
| `disabled` | `boolean` | — | Hard, non-focusable native disabled state (`loading` stays focusable instead). |
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | The button's form behavior. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `nativeButton` | `boolean` | — | Set `false` when `render` swaps the underlying tag away from `<button>` (e.g. a `<div>` or an `<a>`): Base UI then adds `role="button"`, tab focus, and Enter/Space activation. |
| `icon` | `ReactNode` | — | Icon rendered beside the label (inline-start by default), e.g. `<Icon icon={Plus} />`. |
| `iconPosition` | `"start" \| "end"` | `"start"` | Which side of the label the icon sits on. The `loading` spinner takes the same slot. |
| `loading` | `boolean` | — | Shows a spinner in the icon slot, sets `aria-busy`, and makes the button non-interactive. |
