Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Free

Record Button for React

A record and stop button: a red dot that squares off into a stop sign, with a ring that pulses while it records. For React with Base UI, for voice memos, video and screen capture.

Framework
UI library

At rest it's a red dot on a quiet button. Press it and the dot squares off into a stop sign, a red fill grows in behind it and a ring keeps leaving the button's edge. Press again to stop. The pulse runs only while recording, so it says something is happening rather than asking for attention.

Use it for voice notes, video or screen recording, or a voice assistant's talk button; for a pulse that draws the eye at rest, use the Pulse Button. It keeps its own state; pass recording and onRecordingChange to start and stop a real recorder. It wraps the Button, so variant, size and pill work as usual, secondary by default. The dot and the fill change with CSS transitions and the ring is a CSS keyframe animation the install adds to your stylesheet, with no animation library; with reduced motion the ring is left out and the rest only fades.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Usage

import { RecordButton } from "@/components/ui/record-button";
<RecordButton onRecordingChange={toggleRecorder} pill recording={recording}>
  {elapsed}
</RecordButton>

Demos

Variant

The red fill covers any variant while it records, so pick the one that suits the button at rest: here secondary, the default, then outline, destructive and ghost, the last one recording from the start with defaultRecording. Each icon-only button says what it records with startLabel.

Preview width

Controlled

recording and onRecordingChange keep the state outside the button, where a real app starts and stops its MediaRecorder. This one counts the time while it records and lists the last clips.

Preview width

Accessibility

Its name says what a press does, then the visible label: Start recording 0:00 at rest, Stop recording 0:12 while it records. Change the words with startLabel and stopLabel, not with an aria-label, which would hide them and the label. The dot, the fill and the ring are hidden from assistive tech. The label and the stop sign are drawn in the page's background color on the red fill, readable in light and dark.

Enter
Start or stop recording.
Space
Start or stop recording.

FAQ

How do I connect it to MediaRecorder?

Pass recording and onRecordingChange. Start the recorder when onRecordingChange gets true and stop it when it gets false. Leave recording out and the button keeps its own state, starting from defaultRecording.

Can it show the elapsed time?

Pass the time as its children, such as 0:12. The digits are tabular, so the width doesn't jump as the seconds tick, and the name reads Stop recording 0:12. The first demo does this.

Why does its name change when it records?

Because it says what a press does next: Start recording, then Stop recording. With a label of its own, such as Record screen, the name is Start recording Record screen; set startLabel to null to let the label speak alone.

How do I make it round?

Pass pill. Otherwise it takes the Button's radius, like every other button. With no children it's square, icon-lg; with children, lg.

Can I change its colors?

At rest it's the variant you pass, with a red dot. While recording, the fill, the ring and the dot use your theme's destructive color, so a theme with another red changes them all.

How is it different from Pulse Button?

Pulse Button pulses all the time to draw the eye. This one pulses only while it records, as a sign of activity.

What happens with reduced motion?

The ring is left out, and the fill and the dot change with a fade instead of growing and squaring off. The red fill still says it's recording.

API Reference

RecordButton

data-slot="record-button-state"

PropType
recording?boolean

Whether it's recording, to keep that state yourself. Pair it with onRecordingChange.

defaultRecording?boolean

Whether it starts out recording, when you leave recording to the button.

Default
false
onRecordingChange?(recording: boolean) → void

Called with the new state each time the button is pressed.

Parameters

recording – boolean

Returns
void
children?ReactNode

Text beside the dot, such as the elapsed time. Its digits are tabular, so a ticking clock doesn't shift.

focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
startLabel?ReactNode

What the button says it does at rest, for screen readers, before the visible label.

Default
"Start recording"
stopLabel?ReactNode

What the button says it does while recording, for screen readers, before the visible label.

Default
"Stop recording"
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-state?string

"recording" when on is true, otherwise "idle".

All 35 buttons