Push to Talk

Pro

A hold-to-talk key for radios, voice notes and walkie-talkie chat. Live exactly while held, by pointer, keyboard or a global key, with a halo everyone reads as on air and a shake for a press too short to send.

  • Hold the button, or hold T.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add push-to-talk

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Shapes#

circle (the default) is the icon alone, for a console where the key sits beside other controls; name it with aria-label and show that name in a Tooltip. pill says it in words and, while live, counts the time on air. Hold each one.

Sizes#

sm is 32px, for a dense board: it still takes a 40px press (44px on touch) through an invisible hit box, and its halo and shake travel less. md (the default) is 40px, the hit target on its own; lg is 48px, for the main control of a console or a phone's bottom bar. The glyph steps with the size.

Variants#

The idle paint. neutral (the default) is the ink solid: a transmit key is a tool, not a brand moment. primary takes the brand, for a product whose main action is talking. outline is a quiet bordered key, for a board where every row has one. Live is the same for all three: the destructive fill and the halo.

Dispatch board#

A talk key in every unit cell. size="sm" with variant="outline" keeps the board calm at rest; the key held goes live as loudly as any other. Give each one its own aria-label, the unit it calls.

  • 1-ADAM-12Available
    Mission Row
  • 2-LINCOLN-4En route
    Vinewood Blvd
  • 3-MARY-7On scene
    Pillbox Hill
  • 4-KING-9Available
    Vespucci Beach

Minimum duration#

A press shorter than minDuration (450ms by default) is a slip, not a message. It fires onTalkCancel instead of onTalkEnd, both with the duration, and the control shakes. Here the minimum is 600ms. minDuration={0} turns the check off: every hold goes out and nothing shakes.

Tap it, then hold it.

Global shortcut#

shortcut makes a key transmit while held anywhere on the page, the way a radio console works. It stands down while focus is in a text field, while a modifier is held and while a dialog the control is not in is open. The key also lands in aria-keyshortcuts.

Hold V anywhere on the page

Transmitting from a hotkey#

When the transmit is handled elsewhere (a game's global push-to-talk key, a hardware button, a state pushed from the server), pass it as live: the key shows on air, with the destructive fill, the halo and, on the pill, the timer counting from when it turned on, without being pressed. It is a visual state only: it fires no callback, so the code that owns the transmit keeps owning it, and the pointer and keys still work on their own hold. Here the page listens for B and the keys only mirror it.

Hold B to transmit from the page

Labels and icons#

label and liveLabel set the words; timer={false} drops the count. icon and liveIcon swap the glyphs, which cross-fade on the live state.

Disabled#

disabled dims the control and turns its shortcut off. Disabling it while live ends the hold as an interruption: onTalkEnd(ms) fires whatever the length, never a cancel and never the shake. Unmounting it while live does the same, so a closed panel or a switched tab cannot leave the channel open: whatever onTalkStart opened, onTalkEnd (or onTalkCancel for a slip) closes.

API reference#

PushToTalk

A hold-to-talk <button> with aria-pressed, live only while held. Forwards all native <button> props except children (your pointer and key handlers run first); accepts className, merged last.

PropTypeDefaultDescription
shape"circle" | "pill""circle"circle is icon only, named by label unless you pass aria-label; pill shows the words and, while live, the timer.
size"sm" | "md" | "lg""md"sm is 32px for a dense board, md 40px, lg 48px for a console's main control.
variant"neutral" | "primary" | "outline""neutral"The idle paint: the ink solid, the brand fill, or a hairline for dense boards. Live is always the destructive fill.
labelReact.ReactNode"Hold to talk"The words at rest; on the circle, a string label becomes its aria-label.
liveLabelReact.ReactNode"On air"The words while live, before the timer (pill only).
timerbooleantrueShow the time on air (m:ss) beside liveLabel (pill only).
minDurationnumber450A hold shorter than this, in ms, is cancelled: onTalkCancel fires instead of onTalkEnd and the control shakes.
shortcutstring-A key (KeyboardEvent.key, case-insensitive) that transmits while held anywhere on the page. Ignored while typing, with a modifier held, or while a dialog the control is not in is open; also set as aria-keyshortcuts.
iconReact.ReactNode<Microphone weight="fill" />The glyph at rest.
liveIconReact.ReactNode<Waveform weight="bold" />The glyph while live; the two cross-fade.
disabledboolean-Dims the control and turns its shortcut off. Disabling it while live ends the hold.
liveboolean-Shows it on air from outside (a hotkey handled elsewhere), without a press or a callback.
onTalkStart() => void-The hold began.
onTalkEnd(durationMs: number) => void-The hold ended after at least minDuration: the message went out. Also fires, whatever the length, when a live control is disabled or unmounted.
onTalkCancel(durationMs: number) => void-The hold ended before minDuration: nothing went out.
onTalkingChange(talking: boolean) => void-Fires with true on start and false on any end, sent or cancelled.
Data attributeDescription
data-state"idle" at rest, "live" while held.
CSS variableDescription
--shake-distanceHow far the rejection shake travels. Set to 3px.

FAQ#

A Toggle stays on after you let go. Push to Talk is on only while held, so letting go is the send. Use a Toggle for mute; use Push to Talk for a transmit key.