Button
Triggers an action or an event, such as submitting a form, opening a dialog or deleting a record.
function ButtonDemo() {
return <Button>Save changes</Button>
}Usage
import { Button } from "@zenoxai/ui/components/button"
<Button onClick={save}>Save changes</Button>
Use one default (primary) button per view for the main action. Everything else is outline,
secondary or ghost. Write labels as a verb plus an object (“Save changes”, “Delete alert”),
in sentence case.
Variants
<>
<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
</>| Variant | Use for |
|---|---|
default |
The primary action of the view. One per view. |
secondary |
A supporting action next to the primary one. |
outline |
Actions in toolbars, filters and dialogs. |
ghost |
Low-emphasis actions in dense areas: table rows, icon buttons. |
destructive |
Irreversible actions. Confirm them with confirm() first. |
link |
An action that reads as inline text. |
Sizes
default (32px) is the size for forms and toolbars. sm (28px) and xs (24px) fit dense
tables and filter bars; lg (36px) is for empty states and landing surfaces. Input, Select,
Native Select, Number Input, Input Group and Combobox share the sm, default and lg
heights, so a button and a field of the same size line up.
The icon follows the size name: 12px at xs, 14px at sm, 16px at default and lg, in
the square icon-* sizes too.
<>
<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button>Default</Button>
<Button size="lg">Large</Button>
</>With an icon
Mark the icon with data-icon="inline-start" or data-icon="inline-end" so the button
tightens the padding on that side. An icon-only button uses an icon* size and must have an
aria-label.
<>
<Button>
<PlusIcon data-icon="inline-start" />
New alert
</Button>
<Button variant="outline">
Continue
<ArrowRightIcon data-icon="inline-end" />
</Button>
<Button variant="ghost" size="icon" aria-label="Delete alert">
<Trash2Icon />
</Button>
</>Loading
Disable the button, set aria-busy="true" and put a Spinner in place of the icon while the
action runs. A disabled button is dimmed to 50%; a busy one is not, so it reads as working
rather than unavailable. Keep the label: it tells the user what is in progress.
<Button disabled aria-busy="true">
<Spinner data-icon="inline-start" aria-hidden />
Saving
</Button>As a link
Navigation uses a link, not a button. To get the button look on a link, give the <a> the
classes from buttonVariants(). Do not render an <a> through Button’s render prop: Base UI
then gives it role="button", and screen readers announce a button that navigates.
<a href="#usage" className={buttonVariants({ variant: "outline" })}>
Read the usage notes
</a>Accessibility
- A disabled button is removed from the tab order. Set
focusableWhenDisabledwhen the user must still be able to reach it, for example to read a tooltip that explains why. buttonVariants()returns the classes alone. Use it to give a link the button look; the element keeps its own role.
API reference
Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.
Button
Button runs an action on the current page: submit a form, open a dialog, apply a filter. To navigate, use a link styled as a button instead: `<a href="..." className={buttonVariants({ variant: "outline" })}>`. Rendering an <a> through `render` keeps the look but Base UI gives the element role="button", so screen readers announce it as a button.
| Prop | Type | Default |
|---|---|---|
focusableWhenDisabled | booleanWhether the button should be focusable when disabled. | false |
nativeButton | booleanWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | true |
render | ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, ButtonState>Allows you to replace the component's HTML element with a different tag, or compose it with another component. | - |
size | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | nullHeight: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`. | default |
variant | "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | nullVisual weight. `default` is the filled primary action, one per view; `outline` and `secondary` are for the other actions; `ghost` has no fill until hover, for toolbars and icon buttons; `destructive` is for delete and other irreversible actions; `link` looks like inline text. | default |