# Button (/docs/button)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/button" />

## Basic [#basic]

<Preview cssProperties="buttonPlaygroundCssProperties">
  <ButtonExample />

  <Preview.Code>
    {`
          import { Button } from "moduix";

          export function ButtonDemo() {
            return <Button>Save Changes</Button>;
          }
        `}
  </Preview.Code>

  <Preview.CSSProperties>
    {(context) => <ButtonCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <ButtonCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`Button` is composed as one interactive root with optional internal loading slots. In common
usage, compose content as children and optionally enable loading states.

```text
Button
├─ content
└─ loadingIndicator (when loading)
   └─ spinner (default)
```

| Part               | Role                                                                            |
| ------------------ | ------------------------------------------------------------------------------- |
| `Button`           | Interactive root. Handles click, disabled, loading, and focus states.           |
| `content` slot     | Wrapper around button children so content layout stays stable.                  |
| `loadingIndicator` | Slot rendered in loading state; accepts custom indicator via props.             |
| `spinner`          | Default spinner inside `loadingIndicator` when no custom indicator is provided. |

The root exposes `data-disabled` from Base UI and `data-loading` from moduix. Use these attributes
or CSS variables when styling state from CSS.

## Composition [#composition]

Use `Button` behavior props such as `render`, `nativeButton`, `disabled`, and
`focusableWhenDisabled`. Use `className` for the
root button. Use `classNames` for internal slots that are rendered by the component:

```tsx
<Button
  className={styles.button}
  classNames={{
    content: styles.content,
    loadingIndicator: styles.loadingIndicator,
    spinner: styles.spinner,
  }}
/>
```

For submit buttons, pass `type="submit"`. For links, style an `<a>` directly instead of rendering
an anchor through `Button`; Base UI Button intentionally keeps button semantics.

## Examples [#examples]

### Variants [#variants]

Use `variant` to match the intent of the action. For navigation links, style an `<a>` directly instead of rendering a link through `Button`.

<Preview>
  <ButtonVariantsExample />

  <Preview.Code>
    {`
          import { Button } from "moduix";

          export function ButtonVariantsDemo() {
            return (
              <div>
                <Button>Default</Button>
                <Button variant="outline">Outline</Button>
                <Button variant="secondary">Secondary</Button>
                <Button variant="destructive">Destructive</Button>
                <Button variant="destructive-outline">Destructive Outline</Button>
                <Button variant="ghost">Ghost</Button>
                <Button variant="link">Link</Button>
              </div>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Sizes [#sizes]

Use text sizes for standard actions and icon sizes for buttons that only contain an icon.

<Preview>
  <ButtonSizesExample />

  <Preview.Code>
    {`
          import { Button, StarIcon } from "moduix";

          export function ButtonSizesDemo() {
            return (
              <div>
                <Button size="xs">Extra-small</Button>
                <Button size="sm">Small</Button>
                <Button size="md">Medium</Button>
                <Button size="lg">Large</Button>
                <Button size="xl">Extra-large</Button>
                <Button size="icon-sm" variant="outline" aria-label="Small favorite">
                  <StarIcon />
                </Button>
                <Button size="icon-md" variant="outline" aria-label="Favorite">
                  <StarIcon />
                </Button>
                <Button size="icon-lg" variant="outline" aria-label="Large favorite">
                  <StarIcon />
                </Button>
              </div>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Icons [#icons]

Pass icons as children. The component does not lock you into a specific icon set.

<Preview>
  <ButtonIconExample />

  <Preview.Code>
    {`
          import { Button, PlusIcon, StarIcon, ArrowUpRightIcon } from "moduix";

          export function ButtonIconsDemo() {
            return (
              <div>
                <Button>
                  <PlusIcon />
                  Create Item
                </Button>
                <Button size="icon-md" variant="outline" aria-label="Favorites">
                  <StarIcon />
                </Button>
                <Button variant="link">
                  Open Docs
                  <ArrowUpRightIcon />
                </Button>
              </div>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Disabled [#disabled]

Use `focusableWhenDisabled` when the button becomes disabled after focus, for example during a pending action.

<Preview>
  <ButtonDisabledExample />

  <Preview.Code>
    {`
          import { Button } from "moduix";

          export function ButtonDisabledDemo() {
            return (
              <div>
                <Button disabled>Disabled</Button>
                <Button disabled focusableWhenDisabled variant="outline">
                  Focusable Disabled
                </Button>
              </div>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Loading [#loading]

Use the built-in loading props when an action should keep its size while it is pending.

<Preview>
  <ButtonLoadingExample />

  <Preview.Code>
    {`
          import { Button } from "moduix";
          import { useState } from "react";

          export function LoadingButton() {
            const [loading, setLoading] = useState(false);

            return (
              <Button
                loading={loading}
                loadingText="Saving"
                onClick={() => {
                  setLoading(true);
                  setTimeout(() => setLoading(false), 1800);
                }}
              >
                Save Changes
              </Button>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Custom Loading Indicator [#custom-loading-indicator]

Pass `loadingIndicator` to use an icon or spinner from your application.

<Preview>
  <ButtonCustomLoadingIndicatorExample />

  <Preview.Code>
    {`
          import { Button, StarIcon } from "moduix";

          export function CustomLoadingIndicatorDemo() {
            return (
              <Button
                loading
                loadingText="Syncing"
                variant="outline"
                loadingIndicator={<StarIcon className={styles.customLoadingIndicator} />}
              >
                Sync
              </Button>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customLoadingIndicator {
            width: 0.875rem;
            height: 0.875rem;
            animation: var(--animation-spin);
          }
        `}
  </Preview.CSS>
</Preview>

### Custom Styles [#custom-styles]

Pass `className` for the button root and `classNames` for internal slots when styling with CSS Modules, Tailwind CSS, or CSS-in-JS.

<Preview>
  <CustomStylesButtonExample />

  <Preview.Code>
    {`
          import { Button } from "moduix";

          export function CustomStylesButtonDemo() {
            return (
              <Button
                className={styles.customButton}
                classNames={{
                  content: styles.customButtonContent,
                  loadingIndicator: styles.customLoadingIndicatorColor,
                  spinner: styles.customSpinner,
                }}
                loading
                loadingText="Publishing"
              >
                Publish
              </Button>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customButton {
            border-color: var(--color-primary);
            background-color: var(--color-primary);
            color: var(--color-primary-foreground);
            box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
          }

          .customButtonContent {
            letter-spacing: 0.025em;
          }

          .customLoadingIndicatorColor {
            color: var(--color-primary-foreground);
          }

          .customSpinner {
            border-width: var(--border-width-md);
          }
        `}
  </Preview.CSS>
</Preview>
