Send / stop button
Round send button that turns into a stop button with an orbiting ring while a reply streams, then closes the ring with sparks on completion.
Playground
Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.
<UiSendButton demoDuration={3} />Installation
pnpm dlx shadcn@latest add @lumesec/ui-send-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/ui-send-button.jsonUsage
"use client";
import * as React from "react";
import { UiSendButton, type UiSendButtonHandle } from "@/components/lumesec/ui-send-button";
export function Example() {
const button = React.useRef<UiSendButtonHandle>(null);
const timer = React.useRef(0);
return (
<UiSendButton
ref={button}
onSend={() => {
// stand-in for a streamed reply
timer.current = window.setTimeout(() => button.current?.complete(), 2000);
}}
onStop={() => window.clearTimeout(timer.current)}
/>
);
}Behaviour
The arrow launches up and out, a stop square spins in, and a ring orbits while the reply streams. On completion the ring closes, sparks fly and the arrow drops back in.
API reference
Props
Also accepts every prop of <button> (React.ComponentProps<"button">), spread onto the root element.
runningTypebooleanControlled running state. Use with onRunningChange. Setting it from outside switches the look with the icon animation but calls no event callback.
defaultRunningTypebooleanDefaultfalseInitial running state when uncontrolled.
demoDurationTypenumberDefault0Seconds after which a send completes on its own, for demos. 0 means the button waits for complete() or a stop. Read on each send.
onRunningChangeType(running: boolean) => voidCalled when a send, stop or completion changes the running state.
onSendType() => voidCalled when the idle button is pressed or send() is called.
onStopType() => voidCalled when a running send is stopped with the button or stop().
onCompleteType() => voidCalled when a running send completes, after the ring has closed (about 380 ms after complete()).
refTypeReact.Ref<UiSendButtonHandle>Exposes send(), stop() and complete().
Ref
ref receives a UiSendButtonHandle handle with these methods.
send(): voidSwitches to the running state and calls onSend. Ignored while running.
stop(): voidReturns to idle and calls onStop. Ignored when idle and while the ring is closing.
complete(): voidCloses the ring, bursts sparks, returns to idle and calls onComplete. Ignored when idle or already completing.
Keyboard
| Keys | Action |
|---|---|
| Space / Enter | Send when idle, stop while running. |
Accessibility
- A native
<button>whosearia-labelswitches between "Send" and "Stop". - The arrow, stop icon and ring are
aria-hidden. - There is no live region; completion is not announced.
- Reduced motion: the arrow and stop icons swap without animation, the ring does not spin, and no sparks fire on completion.
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-glint, --muted, --foreground and --border.
Notes
- The button carries
data-state="running"ordata-state="idle"for styling. - The button does not stream anything itself: call
complete()throughrefwhen the reply finishes, or setdemoDurationfor a demo. - Once
complete()starts, a stop is ignored until the ring has closed, so a send never reports both outcomes.
Related
Was this page helpful?