Async button
Async button: a promise from onClick shows a pixel spinner, then a drawn check or a shake with "Try again", and returns to its label after 1.8 s.
Playground
Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.
<PxButton variant="default">Save changes</PxButton>
Installation
pnpm dlx shadcn@latest add @lumesec/px-buttonFirst timeRegister the @lumesec registry once, or install by URL+
Adds @lumesec to your components.json:
pnpm dlx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.jsonOr skip the setup and install by URL:
npx shadcn@latest add https://elements.lumesec.ai/r/px-button.jsonUsage
import { PxButton } from "@/components/lumesec/px-button";
const saveSettings = () => new Promise<void>((resolve) => setTimeout(resolve, 1300));
export function Example() {
return <PxButton onClick={() => saveSettings()}>Save changes</PxButton>;
}Behaviour
Return a promise from onClick (or pass one to run() on its ref) and it shows a pixel spinner, then a drawn check or a shake with “Try again”. Inside a form, type="submit" submits it.
API reference
Props
Also accepts every prop of <button> (React.ComponentProps<"button">), spread onto the root element.
childrenTypestringDefault"Save"The label.
variantType"default" | "secondary" | "danger"Default"default"default is the accent button, secondary a bordered surface button, danger uses the destructive colour.
loadingTypebooleanDefaultfalseShows the loading state while true, for loading driven from outside. Turning it on calls onStart; turning it off returns to the label.
loadingTextTypestringDefault"Working…"Label while loading.
successTextTypestringDefault"Done"Label after the task resolves.
errorTextTypestringDefault"Try again"Label after the task rejects.
typeType"button" | "submit" | "reset"Default"button"Native button type. submit submits its form, except while loading.
onClickType(event: React.MouseEvent<HTMLButtonElement>) => void | Promise<unknown>Click handler. When it returns a promise, the button runs it like run(). Not called while loading.
onStartType() => voidCalled when the loading state begins.
onSuccessType() => voidCalled when the task resolves.
onErrorType(error: unknown) => voidCalled with the rejection reason when the task fails.
Ref
ref receives a PxButtonHandle handle with these methods.
run<T>(task: () => Promise<T>): Promise<T>Shows the loading state while task runs, then the success or error state for 1.8 s before returning to the label. Resolves with the task's result or rethrows its error. It does not check disabled.
focus(options?: FocusOptions): voidMoves focus to the button.
Accessibility
- A native
<button>; its accessible name is the visible label. - The label is an
aria-live="polite"region, so the loading, success and error texts are announced. aria-busyistruewhile loading.- The pixel icon canvas is
aria-hidden. - A native button. With
type="submit"a click submits its form, which runs validation; clicks while loading neither submit nor callonClick. - Reduced motion: the label swaps without rolling and the error shake is skipped; the pixel spinner and check still draw.
Theming
Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from --lumesec. See Theming.
This component reads --lumesec, --lumesec-foreground, --lumesec-success, --destructive, --card, --border and --foreground.
Notes
- The label rolls between states. Pass the label as a string; it is not a slot for markup.
- The root carries
data-stateset toloading,successorerroroutside the idle state, anddata-variant. refis the handle, not the DOM button.
Related
Was this page helpful?