Button triggers an action, such as saving a form, opening a dialog, or deleting an item. Use
Link when the interaction takes someone to another URL or route.
Use Button for an action, such as saving, submitting, or opening a dialog.
Don't
Navigate from a Button. It loses native link semantics and browser link behaviours. Use a Link with appearance="button" instead.
Don't
Put a high Button on a solid fill of its own colour: accent.solid for a neutral tone or danger.solid for a danger tone. Its fill blends into the background. Place the action on a contrasting surface instead.
button is the default appearance. It has control sizing, truncates its label, and supports content
slots. Set appearance="text" when an action should use text-link presentation. Text Buttons wrap
and do not support a size, block layout, or content slots. Text Buttons are neutral at low and
standard prominence and use the accent colour at high prominence. Low prominence has no underline at
rest and gains one on hover and press. Standard and high prominence are underlined at rest and lose
the underline on hover and press. Keyboard focus shows the focus ring.
standard is the default. Choose low for quiet secondary actions and high for the primary
action in a group. For ordinary actions, high prominence uses the theme’s primary accent treatment.
Pass inlineSize="full" when the button should fill the inline size of its container. A typical
case is a primary action at the end of a narrow form or panel.
Use startContent or endContent for non-interactive adornments beside the label, such as an icon,
badge, count, or keyboard hint. Nested interactive controls are unsupported. Button sizes nested
icons for you, so an Icon needs no size prop. Pass one only to override it. Use
IconButton only when the icon is familiar without a text label.
onPress handles the interaction. pressAction performs the resulting operation. When both are
set, onPress runs first, then pressAction.
Prefer a React form Action when the operation is a form submission. Pass the Action to Luke UI's
Form and the form's pending state to Button. This example uses React's
useActionState.
import { Button } from '@luke-ui/react/button';import { Form } from '@luke-ui/react/form';import { useActionState } from 'react';export default () => { const [, formAction, isPending] = useActionState(save, null); return ( <Form action={formAction}> <Button isPending={isPending} type="submit"> Save </Button> </Form> );};async function save(_state: null, _formData: FormData) { await new Promise((resolve) => { setTimeout(resolve, 1200); }); return null;}
Use pressAction for Button-owned operations that are not form submissions. Combining pressAction
with type="submit" is usually the wrong pattern, because both paths may run.
pressAction creates pending state automatically. Explicit isPending shows a spinner immediately.
An Action-owned spinner waits briefly so a fast Action never flashes one.
import { Button } from '@luke-ui/react/button';export default () => { return <Button pressAction={save}>Save changes</Button>;};async function save() { await new Promise((resolve) => { setTimeout(resolve, 1200); });}
Set isPending for externally owned pending state, such as a parent mutation or router transition.
Pending buttons stay focusable and ignore further presses.
The visible label provides the accessible name, so most buttons do not need aria-label. Write a
label that describes the action, such as “Save changes” or “Delete account”, rather than a vague
label such as “OK”.
The spinner is hidden from assistive technology. Pending state is announced through the button’s
pending semantics, so the label does not need to change while pending.
ButtonProps also accepts compatible DOM and ARIA attributes and event handlers for its rendered element.
Name
Type
Default
appearance
"button" | "text"
Default: button
Sets whether the control uses button or text-link styling.
endContent
ReactNode
Default: —
Non-interactive adornment shown after the label.
inlineSize
'auto' | 'full'
Default: auto
Use full to fill the available inline size.
isPending
boolean
Default: false
Externally owned pending state. When true, the button is non-interactive and shows a spinner
immediately. Prefer pressAction for Button-owned operations.
pressAction
Default: —
Button-owned operation run as a React Action. The button becomes pending automatically until
the Action settles. onPress handles the interaction. pressAction performs the resulting
operation. Prefer a native <form action> when the operation is a form submission.
prominence
Default: standard
Controls the strength of the control's visual treatment, from low to high.
size
'small' | 'medium'
Default: medium
Sets the size of the control.
startContent
ReactNode
Default: —
Non-interactive adornment shown before the label.
tone
"danger" | "neutral"
Default: neutral
Sets the visual tone. Use danger for destructive actions.