Audio recorder
Record a voice note and review it before sending.
Preview
Design guidance
Record requests microphone access after the user acts. Pause preserves the current take. Finish waits for the final nonempty Blob and opens a local Audio player preview. Discard releases the capture and its preview URL. Permission is never requested on mount.
The official WaveSurfer Record plugin owns capture, pause, resume, and live rendering. Ghost supplies the controls and state presentation. The caller owns Send, upload, transcription, retention, and service errors. A completed local capture is not a delivery acknowledgment.
Installation
Package consumers can install wavesurfer.js, @fix-webm-duration/fix and @fix-webm-duration/parser, then import from @ghost-ui/core/audio. Basic component imports keep the audio dependencies optional.
Recording needs a secure context, browser MediaRecorder support, a microphone, and permission. The browser selects the recording format unless the application provides a supported MIME type.
Download example files — JSX recipes, demo styles and any sample assets. Install the component first.
Basic usage
Structures
AudioRecorder.Root provides context to RecordButton, PauseResume, StopButton, DiscardButton, Time, and Preview. Use useAudioRecorder() for state and commands such as cancel; there is no exported CancelButton. appearance="embedded" removes the border and padding inside a host surface.
The convenience root supplies the complete idle, permission, capture, paused, finalizing, review, and error composition. Use useAudioRecorder() and the same parts for an embedded composer. The hook exposes state, previewSrc, record, cancel, pauseResume, stop, and discard.
Examples
Record, pause, stop, and discard
The default controls preserve the current take while paused, finalize it for review, and discard both capture and preview.
Record pause stop and discard
Custom controls
Compose the exported parts under AudioRecorder.Root; the same context drives the capture and preview.
Permission, device, and capture errors
Click Record and deny microphone access, or disconnect the device, to see the recovery state. Permission is requested only after the action; try again after restoring access.
Permission device and capture errors
Record moves from idle through requesting to recording. Pause and Resume preserve the take. Finish enters stopping, then ready after final data arrives. Cancel during permission and Discard during capture invalidate pending work. Late permission results cannot publish a recording after cancellation.
Denied permission, unsupported capture, unavailable devices, and empty final data show an error. An externally ended microphone preserves available audio for review and marks the completion reason device-ended. The application can decide whether to retain that partial take. Discard and unmount stop microphone use and revoke only the preview URLs this root owns. A Blob already passed to the caller remains caller-owned.
Embedded capture
Use the host Composer surface and preserve the typed draft while recording or removing a clip. Finish produces a local review; Send remains explicit.
API reference
AudioRecorder.Rootappearance | 'standalone' | 'embedded' | 'standalone' | Use embedded inside an existing Composer surface. |
|---|
label | string | 'Voice note' | Visible and accessible capture name. |
|---|
mimeType | string | — | Optional recording MIME type supported by the browser. |
|---|
audioBitsPerSecond | number | — | Optional requested recording bitrate. |
|---|
constraints | MediaTrackConstraints | — | Optional constraints passed to the microphone request. |
|---|
onRecordingComplete | (blob: Blob, durationSeconds: number, details: AudioRecordingDetails) => void | — | Reports a nonempty finalized capture. reason is stopped or device-ended. |
|---|
onStateChange | (state: AudioRecorderState) => void | — | Reports status, duration in seconds, message, and actual MIME type. |
|---|
onError | (error: Error) => void | — | Reports recording setup or finalization errors. |
|---|
children | React.ReactNode | — | Optional caller composition using the same context and parts. |
|---|
Root accepts native div attributes and forwards its ref. RecordButton accepts Button appearance and disabled props. Time accepts native span props. Preview uses AudioPlayer with the owned local recording URL, compact Seek, PlayPause, Time and Mute. It accepts AudioPlayer props and custom children except src, label and measured capture duration, which remain recorder-owned. Live capture stays read-only; only the completed result has seek controls. Hooks and state types have no AI SDK dependency.
Recorder measures the completed file from decoded audio samples, then writes that measured length into WebM duration metadata before it creates the preview URL or calls onRecordingComplete. It replaces stale duration placeholders as well as missing values. Audio frames stay unchanged. Other recording formats keep their original bytes. The stopping state covers this local work; discard or unmount cancels publication of a late result. During capture, duration reports elapsed capture time. After finalization, duration and the completion callback report the playable file length.