Button

Pill button that presses down under your finger, in eight styles from solid to glass, with a built-in loading state that swaps in place while an action runs.

Preview

Props Changes apply to the live preview above.

Installation

npx @lumioui/cli add button

Usage

import { Button } from "@/components/ui/button/button";
<Button />

Examples

Glass

Glass CTAs over a full-bleed photo, where glass earns its keep.

Sizes

Each size where it lives in a real app: sm as app-store row pills, md as profile actions, lg as the full-width screen CTA.

Styles

Every voice on one real surface, loudest to quietest: the filled accent CTA, a secondary beside it, tinted and outline for inline choices, a link, and destructive kept apart at the bottom.

API reference

Prop Type Default
onPress
((e: GestureResponderEvent) => void) … ((e: GestureResponderEvent) => void) | ((e: GestureResponderEvent) => Promise<unknown>) | null

Press handler — may return a Promise. With asyncTracking (default) the button enters the loading state until the promise settles. Declared as a union of signatures rather than void | Promise<unknown> so void keeps its leniency — handlers returning other values (e.g. a timeout id) still typecheck.

—
variant
"default" | "destructive" +6 "default" | "destructive" | "outline" | "tinted" | "secondary" | "ghost" | "link" | "glass"

Visual style — "glass" is the glass material button.

"default"
tone "accent" | "inverse" | "surface"

Fill voice for the default variant — inverse is the near-black CTA, surface a bordered card-colored button. Default "accent".

"accent"
radius
"pill" | "xl" +2 "pill" | "xl" | "lg" | "md"

Corner radius. Default "pill".

"pill"
size
"sm" | "md" +2 "sm" | "md" | "lg" | "icon"

Size preset.

"md"
loading boolean

Swap the label for busy dots and block presses while true.

false
asyncTracking boolean

Auto-track async onPress — when onPress returns a Promise, the button automatically enters loading state until it resolves. Default true.

true
sheenOnResolve boolean

@deprecated No effect: busy resolves by swapping the label back in place.

—
haptic "light" | "medium" | "heavy" | false

Haptic feedback on press. Default "light"; false to disable.

"light"
pressedScale number

Override the press-dip scale (1 = none). ButtonGroup sets this to 1 on attached children — a scaled segment opens a gap in the weld.

—
className string

Extra NativeWind classes for the container.

—
textClassName string

Extra classes for the label.

—
icon React.ReactNode

Icon rendered before the label.

—
ripple boolean

Ignite a glow from the touch point that spreads as it dims (ripple). Off by default; skipped under reduced motion.

false
children React.ReactNode —