Button
Triggers a click action usually performed by the user to trigger an event such as submitting a form or closing a dialog.
Anatomy
Import and assemble the component:
1import { Button } from '@raystack/apsara'23<Button />
Usage
A button's appearance comes from three independent props — variant, color and size. The rest change what it does.
Variant
Four styles, in descending order of emphasis. Use solid for the one primary action on a screen, outline for secondary actions beside it, and ghost or text for actions that sit inside dense UI where a filled button would shout. Default is solid.
1<Flex gap={9}>2 <Button variant="solid">Solid</Button>3 <Button variant="outline">Outline</Button>4 <Button variant="ghost">Ghost</Button>5 <Button variant="text">Text</Button>6</Flex>
Color
Color carries meaning, not decoration. danger for destructive actions, success for confirmations, neutral for actions that shouldn't compete for attention, accent for everything else. Default is accent.
1<Flex gap={9}>2 <Button color="accent">Accent</Button>3 <Button color="danger">Danger</Button>4 <Button color="neutral">Neutral</Button>5 <Button color="success">Success</Button>6</Flex>
Size
Two sizes. normal is the default and fits most layouts. Use small inside toolbars, table rows, and other dense surfaces.
1<Flex gap={9} align="center">2 <Button size="small">Small</Button>3 <Button size="normal">Normal</Button>4</Flex>
With icons
Pass leadingIcon, trailingIcon, or both. A leading icon reinforces the action; a trailing icon usually signals what happens next, like an arrow for navigation or a chevron for a menu.
1<Flex gap={9}>2 <Button variant="solid" color="accent" leadingIcon={<>I</>}>3 With leading icon4 </Button>5 <Button variant="solid" color="accent" trailingIcon={<>O</>}>6 With trailing icon7 </Button>8 <Button9 variant="solid"10 color="accent"11 leadingIcon={<>I</>}12 trailingIcon={<>O</>}13 >14 With both icons15 </Button>
Loading
loading swaps the content for a spinner and blocks further clicks, so an in-flight action can't be fired twice. Add loaderText to say what's happening — useful when the wait is longer than a second or two.
1<Flex gap={9}>2 <Button variant="solid" loading>3 Button4 </Button>5 <Button variant="solid" loading loaderText="Loading...">6 Button7 </Button>8 <Button variant="outline" loading loaderText="Loading...">9 Button10 </Button>11</Flex>
Disabled
disabled stops the button responding to any interaction. Default is false.
1<Flex gap={9}>2 <Button variant="solid" disabled>3 Solid4 </Button>5 <Button variant="outline" disabled>6 Outline7 </Button>8 <Button variant="ghost" disabled>9 Ghost10 </Button>11 <Button variant="text" disabled>12 Text13 </Button>14</Flex>
Render as another element
render swaps the underlying element while keeping the button's styling and behavior. Use it for links that should look like buttons, so the browser still gives you middle-click, right-click, and open-in-new-tab.
1<Flex gap={9} align="center">2 <Button render={<a href="/docs/components/link" />}>3 Rendered as a link4 </Button>5 <Button variant="outline" render={<a href="/docs/components/link" />}>6 Outline link7 </Button>8</Flex>
When the rendered element isn't a real <button>, nativeButton defaults to false so the correct role and keyboard handling are applied.
API Reference
Renders a clickable button element.
Prop
Type
Slots
Every rendered part carries a stable data-slot attribute for styling and testing:
| Slot | Element |
|---|---|
button | The root <button> element |
button-leading-icon | Wrapper around leadingIcon (when set and not loading) |
button-trailing-icon | Wrapper around trailingIcon (when set and not loading) |
button-loader | The loading spinner (when loading) |
button-loader-text | Wrapper around loaderText (when loading with loaderText) |
Accessibility
- Uses the native
<button>element so keyboard activation with Enter and Space works out of the box. - Disabled state is communicated via the native
disabledattribute. - Loading state sets
aria-busy="true"on the button so screen readers announce the busy state. The internal spinner is rendered witharia-hidden="true"to avoid double-announcing. - Respects motion preferences: button loader rotation is enabled only when
prefers-reduced-motion: no-preference.