Skip to content

Audio Recorder

Record a voice note or an audio clip, listen back on its waveform, then save it or discard it.

import { AudioRecorder, AudioRecordingInfo } from '@youj/react'

Experimental: what this label means

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use an audio recorder wherever people leave a voice note or a short clip: a message composer, a support form, a spoken answer in a survey. The first press of the record button asks for the microphone. While it records, the sound scrolls past as bars, a timer counts up, and pause and stop sit beside it. A stopped take opens on a waveform scrubber with play, its length, discard and Save, which hands you the take. The recorder keeps one size through every state: the controls change, the box doesn't.

import { AudioRecorder } from '@youj/react'

<AudioRecorder
  maxDuration={60}
  onRecordingComplete={(blob, { duration, mimeType }) => upload(blob, { duration, mimeType })}
/>

// A recording you already have opens on playback.
<AudioRecorder value={savedNote} />

It records with MediaRecorder, in mimeType when the browser supports it and otherwise in the first of WebM with Opus, MP4 and Ogg that it does (Safari records MP4). Save is an async button: it awaits onRecordingComplete, which gets the Blob with its duration in seconds and its mimeType, shows a spinner and then a check with "Saved", and the take becomes read-only: playback only, with a quiet Saved mark where the actions were. If the promise rejects, the button shows the error and the take stays unsaved, so it can be saved again. Pass saved={false} to give a saved take back its discard and Save (to re-record, say); saved is controlled when you pass it. Nothing plays by itself: playback starts only from the play button.

Discard keeps the take for a few seconds behind an Undo link; onDiscard fires once the discard is final (the window closes, a new take starts, or the recorder unmounts). A take passed as value (a Blob or a URL) opens saved, for playback only.

The microphone, the analyzer and the recorder are released when a take stops and when the recorder unmounts, so the browser's recording indicator goes out. getStream replaces the microphone with any MediaStream (a chosen device, a tab's audio, or the simulated voice these demos use).

If the browser refuses the microphone, finds none, or can't record at all, the caption says so and what to do, and the record button tries again. A permission refused earlier shows before the first press where the browser reports it.

Reference

PropsonRecordingComplete, maxDuration, mimeType, value, saved, onDiscard… (12)

Props

AudioRecorder props
PropType
onRecordingComplete(blob: Blob, info: AudioRecordingInfo) => unknown | Promise<unknown>

Save: the take, its length and type. Awaited: a rejection keeps the take unsaved and shows the error on the button.

–
maxDurationnumber

The longest take, in seconds: a ring counts down and recording stops there.

–
mimeTypestring

The container to record in, when the browser supports it; otherwise the first of WebM/Opus, MP4 and Ogg that it does.

–
disabledboolean–
sizeExtract<Size, 'sm' | 'default'>

default 'default'

'default'
valueBlob | string | null

A recording to show, as a Blob or a URL: it opens on playback, saved.

–
savedboolean

Whether the take is saved, and so read-only (controlled). Default: true once Save succeeds or a value opens; false gives back discard and Save.

–
onDiscard() => void

When a discard is final: the Undo window closed, a new take started, or the recorder left.

–
soundboolean

Plays toggleOn and toggleOff around a take, and Save's success or error.

default true

true
getStream() => Promise<MediaStream>

Where the sound comes from. Default: the microphone through getUserMedia.

–
labelstring

The group's name. Default: the labels' "Audio recorder".

–
localestring

Formats the times. Default: the YoujProvider locale.

–
Other props pass through: extends ComponentProps<'div'>.
StylingParts and data attributes

Styling

The root audio-recorder carries data-state (idle, requesting, recording, paused, recorded or saved), data-size (sm or default) and data-disabled. Its parts are audio-recorder-stage (the live wave, audio-recorder-wave, or under reduced motion the meter, audio-recorder-meter, then the scrubber), audio-recorder-controls, audio-recorder-primary (record, stop, play), audio-recorder-ring, audio-recorder-dot, audio-recorder-timer, audio-recorder-remaining, audio-recorder-pause, audio-recorder-time, audio-recorder-discard, audio-recorder-save (the AsyncButton), audio-recorder-saved (the read-only mark), audio-recorder-caption and audio-recorder-error. A lit meter bar has data-yj-lit.

MotionLive wave, countdown · reduced motion

Motion

Motion tokens
MomentTokens
Live waveone bar per twentieth of a second, sliding toward the start between samples, so the strip scrolls with the sound
duration.instant

80ms · every frame

80msevery frame
Recording dotbreathes while recording and holds still while paused
duration.slow

340ms

340ms
Countdown ringruns down with the time left; accent, then danger for the last ten seconds
duration.instant

80ms · every frame

80msevery frame
Take opensthe scrubber’s bars rise from the midline from the start
duration.deliberate

560ms

560ms

Reduced motion

A still level meter that lights up with the sound instead of the scrolling bars; the dot holds still.

Reduced motion

The wave doesn't scroll: a row of bars of rising height lights up to the current level, so you can still see that the microphone hears you. The recording dot holds still, and the scrubber's bars are drawn in place.

AccessibilityNames, timer, announcements, keyboard

Accessibility

  • The recorder is a group named by label (default "Audio recorder"). Every button is named: Start recording, Stop recording, Pause recording, Resume recording, Play, Pause, Discard recording, Save.
  • The record button keeps its place and its focus as it becomes stop and then play, so Space starts a take, stops it and plays it back without moving.
  • The recording clock is a role="timer" named "Recording time"; it is not announced every second.
  • Starting, pausing, stopping (with the take's length), reaching the limit, discarding and restoring are announced politely, as are the microphone messages.
  • After a discard, an undo or a save, focus returns to the record or play button.
  • Save announces its progress and outcome through the AsyncButton ("Saving…", "Saved").
  • The waves are decorative; the scrubber is a slider with the time and length as its value.
  • Time runs left to right in every script, like the scrubber; the controls follow the page's direction.
  • Its words come from the labels (audioRecorder…, with play, pause and undo), translated by the language packs.