How to Use

Button

Buttons are interactive elements that trigger actions when clicked, tapped, or activated via keyboard. They are the most common UI control and come in three visual variants (solid, soft, bordered), seven semantic color roles (default, primary, secondary, info, success, warning, danger), and six sizes (xs through xl). Buttons support loading spinners, disabled states, icon slots (startContent and endContent), a polymorphic as prop for rendering as a or button elements, and a ripple animation on press. The ButtonGroup wrapper arranges related buttons together horizontally with connected borders.

import {Button} from "oks-ui"

Variants

Button has three visual variants controlled by the variant prop. "solid" (default) renders a filled button with strong contrast — use it for primary calls-to-action. "soft" renders a semi-transparent filled style that works well for secondary actions alongside a solid primary button. "bordered" renders an outline-only style — ideal for tertiary actions where you want to minimize visual weight, such as cancel buttons in dialog footers.

<Button variant="solid">Solid</Button>
<Button variant="soft">Soft</Button>
<Button variant="bordered">Bordered</Button>

Colors

The color prop applies a semantic color role that communicates the button's purpose. "default" renders in neutral tones — use for generic actions. "primary" uses the brand accent color — use for the main call-to-action on a page. "success" renders green — use for confirm or save actions. "danger" renders red — use for destructive actions like delete. "warning" renders amber — use for cautionary actions. "info" renders blue — use for informational actions. "secondary" renders a subdued tone — use for less prominent actions alongside primary. Each color adapts across all three variants automatically.

<Button color="default">Default</Button>
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="info">Info</Button>
<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
<Button color="danger">Danger</Button>

Sizes

The size prop controls the button's dimensions, font size, and padding in a consistent scale. "xs" and "xs-sm" are suited for compact toolbars and table action columns. "sm" works well for inline actions alongside text. "md" is the default size for most use cases — balances touch target size with density. "lg" and "xl" are suited for hero sections and prominent calls-to-action where you want the button to stand out visually.

<Button size="xs">XS</Button>
<Button size="xs-sm">XS-SM</Button>
<Button size="sm">SM</Button>
<Button size="md">MD</Button>
<Button size="lg">LG</Button>
<Button size="xl">XL</Button>

Loading and Disabled

Use isLoading to show a loading spinner inside the button while an async operation is in progress. When isLoading is true, the button is automatically disabled — no need to set both. The button maintains its width so the layout doesn't shift when the spinner appears. Use isDisabled to prevent interaction entirely without showing a spinner — useful for buttons that should not be clickable until a prerequisite condition is met (e.g., a form with incomplete fields).

<Button isLoading color="primary">Saving...</Button>
<Button isDisabled>Cannot Proceed</Button>
<Button isDisabled variant="soft">Unavailable</Button>

Icons and Icon-Only

Add icons or custom elements before or after the button label using startContent and endContent. These accept any ReactNode — typically an inline SVG or a span containing an icon character. For icon-only buttons (common in toolbars and action menus), set isIconOnly to true — this renders the button as a square with equal width and height. Always provide an aria-label on icon-only buttons so screen readers can announce the action. The isIconOnly prop automatically hides the button's label slot.

<Button color="primary" startContent={<span>✉</span>}>
  Send Email
</Button>

<Button color="danger" endContent={<span>→</span>}>
  Delete
</Button>

<Button isIconOnly color="secondary" aria-label="Settings">
  <span>⚙</span>
</Button>

ButtonGroup

ButtonGroup wraps multiple Button components into a connected horizontal group. Adjacent buttons share borders for a unified appearance. Use it for segmented controls, toggle groups, or splitting related actions (e.g., pagination with Previous and Next). The color prop on ButtonGroup is inherited by all child buttons, but individual buttons can override it. ButtonGroup works with all variants and sizes consistently.

<ButtonGroup color="primary">
  <Button>Left</Button>
  <Button>Center</Button>
  <Button>Right</Button>
</ButtonGroup>

Accessibility

Button renders as a native <button> element by default, providing built-in keyboard and screen reader support. When an href prop is provided, Button automatically renders as an <a> element for navigation. The disabled state sets the disabled attribute, preventing focus and activation. Loading state sets aria-busy="true" to signal activity to assistive technologies. Icon-only buttons MUST include an aria-label that describes the action (e.g., aria-label="Edit user"). The ripple effect uses prefers-reduced-motion to respect user accessibility settings.

<Button isIconOnly aria-label="Edit user" color="primary">
  <span>✏</span>
</Button>

Polymorphic as Prop

The as prop allows Button to render as any HTML element or React component. When href is provided, Button automatically renders as an <a> tag with proper anchor behavior. You can also use as="span" to render a non-interactive styled button for decorative purposes, or as={Link} (from Next.js or React Router) for client-side navigation. The as prop preserves all Button styling while changing the underlying DOM element.

<Button as="a" href="https://example.com" color="primary">
  Visit Site
</Button>

Props Reference

All available props for Button. See the full API reference page for interactive playground examples.

PropTypeDefaultDescription
variant"solid" | "soft" | "bordered""solid"Controls the visual style of the button.
color"default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger""default"Semantic color role for the button.
colorDepth50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950Specific palette shade override.
size"xs" | "xs-sm" | "sm" | "md" | "lg" | "xl""md"Controls the button dimensions and font size.
radius"none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | "full""md"Border radius override. Inherits from context.
shadow"none" | "xs" | "xs-sm" | "sm" | "md" | "lg""none"Shadow depth for the button.
isDisabledbooleanfalseDisables the button and prevents interaction.
isLoadingbooleanfalseShows a loading spinner and disables interaction.
isIconOnlybooleanfalseRenders as a square icon button. Requires aria-label.
fullWidthbooleanfalseStretches the button to fill its container width.
disableAnimationbooleanfalseOpt out of all animation effects.
disableRipplebooleanfalseDisables the ripple effect on press.
hrefstringIf provided, renders as an <a> element.
startContentReactNodeContent rendered before the button label.
endContentReactNodeContent rendered after the button label.
type"button" | "submit" | "reset""button"Native button type attribute.
asElementType"button"Polymorphic root element. Auto-set to 'a' when href is provided.
onPress(e: PressEvent) => voidCalled when the button is pressed (click, Enter, or Space).

Best Practices

  • Use color="primary" for the main call-to-action on every page — it should be visually prominent and usually placed once per view
  • Use variant="soft" for secondary actions; they sit visually behind the primary solid button without competing
  • Always set aria-label on isIconOnly buttons — screen readers cannot infer intent from an icon alone
  • Use isLoading during async operations instead of manually toggling isDisabled; it provides visual feedback and prevents double-clicks
  • When using Button inside a <form>, set type="submit" on the primary submit button and type="button" on cancel/dismiss buttons to prevent unintended form submission
  • ButtonGroup works best with 2-4 buttons; for larger groups, consider a Dropdown or segmented control pattern
  • The ripple effect can be disabled globally via disableRipple prop or per-button with the same prop
  • fullWidth stretches the button to fill its container — useful for mobile layouts and card-based actions