Skip to content
NewService Map: 30 components for hosts, ports and routes
<UiSendButton />ReactControlsFree

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.

View as Markdown
<UiSendButton />

Trypress send. It completes after 3 s, or press stop to cancel.

Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

Loading <UiSendButton />
Props
demoDurationlive
defaultRunningremounts
React
<UiSendButton demoDuration={3} />

Installation

pnpm dlx shadcn@latest add @lumesec/ui-send-button
First 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}.json

Or skip the setup and install by URL:

npx shadcn@latest add https://elements.lumesec.ai/r/ui-send-button.json

Usage

ui-send-button-example.tsx
"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.

runningTypeboolean

Controlled running state. Use with onRunningChange. Setting it from outside switches the look with the icon animation but calls no event callback.

defaultRunningTypebooleanDefaultfalse

Initial running state when uncontrolled.

demoDurationTypenumberDefault0

Seconds 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) => void

Called when a send, stop or completion changes the running state.

onSendType() => void

Called when the idle button is pressed or send() is called.

onStopType() => void

Called when a running send is stopped with the button or stop().

onCompleteType() => void

Called 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(): void

Switches to the running state and calls onSend. Ignored while running.

stop(): void

Returns to idle and calls onStop. Ignored when idle and while the ring is closing.

complete(): void

Closes the ring, bursts sparks, returns to idle and calls onComplete. Ignored when idle or already completing.

Keyboard

KeysAction
Space / EnterSend when idle, stop while running.

Accessibility

  • A native <button> whose aria-label switches 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" or data-state="idle" for styling.
  • The button does not stream anything itself: call complete() through ref when the reply finishes, or set demoDuration for a demo.
  • Once complete() starts, a stop is ignored until the ring has closed, so a send never reports both outcomes.

Was this page helpful?