useMediaRecorder
Records audio/video streams using the MediaRecorder API.
About
React hook for audio/video recording using the MediaRecorder API. Provides methods to record, pause, resume, and stop media capture from any MediaStream source.
Installation
npm install rooksUsage
import { useMediaRecorder } from "rooks";
import { useEffect, useState } from "react";
function AudioRecorder() {
const [stream, setStream] = useState<MediaStream | null>(null);
useEffect(() => {
let activeStream: MediaStream | null = null;
void navigator.mediaDevices.getUserMedia({ audio: true }).then((next) => {
activeStream = next;
setStream(next);
});
return () => {
activeStream?.getTracks().forEach((track) => track.stop());
};
}, []);
const {
startRecording,
stopRecording,
pauseRecording,
resumeRecording,
recordingState,
dataUrl,
error,
isSupported,
} = useMediaRecorder(stream, { mimeType: "audio/webm" });
if (!isSupported) {
return <div>MediaRecorder API not supported in this browser</div>;
}
return (
<div>
<h2>Audio Recorder</h2>
<p>
Status: <strong>{recordingState}</strong>
</p>
<div>
<button
onClick={startRecording}
disabled={recordingState === "recording" || !stream}
>
Start Recording
</button>
<button
onClick={pauseRecording}
disabled={recordingState !== "recording"}
>
Pause
</button>
<button
onClick={resumeRecording}
disabled={recordingState !== "paused"}
>
Resume
</button>
<button
onClick={stopRecording}
disabled={recordingState === "idle" || recordingState === "stopped"}
>
Stop
</button>
</div>
{dataUrl && (
<div>
<h3>Recording</h3>
<audio src={dataUrl} controls />
<a href={dataUrl} download="recording.webm">
Download Recording
</a>
</div>
)}
{error && <p style={{ color: "red" }}>Error: {error.message}</p>}
</div>
);
}Return value
Returns an object with the following properties:
| Property | Type | Description |
|---|---|---|
| startRecording | () => void | Start recording |
| stopRecording | () => void | Stop recording |
| pauseRecording | () => void | Pause recording |
| resumeRecording | () => void | Resume recording |
| recordingState | "idle" | "recording" | "paused" | "stopped" | Current recording state |
| data | Blob | null | Recorded data as a Blob |
| dataUrl | string | null | URL to the recorded data |
| error | Error | null | Any error that occurred |
| isSupported | boolean | Whether MediaRecorder API is supported |
Recording States
"idle"- Not recording"recording"- Currently recording"paused"- Recording paused"stopped"- Recording stopped (data available)
MediaRecorder Options
The hook accepts an optional options parameter:
| Option | Type | Description |
|---|---|---|
| mimeType | string | MIME type for the recording |
| audioBitsPerSecond | number | Audio bits per second |
| videoBitsPerSecond | number | Video bits per second |
| bitsPerSecond | number | Overall bits per second |
Examples
import { useState } from "react";
import { useMediaRecorder } from "rooks";
export default function AudioRecorder() {
const [stream, setStream] = useState<MediaStream | null>(null);
const recorder = useMediaRecorder(stream, { mimeType: "audio/webm" });
async function requestMicrophone() {
setStream(await navigator.mediaDevices.getUserMedia({ audio: true }));
}
return (
<section>
<p>Recorder state: {recorder.recordingState}</p>
<button type="button" onClick={requestMicrophone}>
Enable microphone
</button>
<button
type="button"
onClick={recorder.startRecording}
disabled={!stream}
>
Record
</button>
<button type="button" onClick={recorder.stopRecording}>
Stop
</button>
</section>
);
}Video Recording
function VideoRecorder() {
const [stream, setStream] = useState(null);
const startCamera = async () => {
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
});
setStream(mediaStream);
};
const { startRecording, stopRecording, recordingState, dataUrl } =
useMediaRecorder(stream, { mimeType: "video/webm" });
return (
<div>
{!stream && <button onClick={startCamera}>Start Camera</button>}
{stream && (
<>
<video
ref={(video) => {
if (video && stream) video.srcObject = stream;
}}
autoPlay
muted
/>
<button onClick={startRecording}>Record</button>
<button onClick={stopRecording}>Stop</button>
</>
)}
{dataUrl && <video src={dataUrl} controls />}
</div>
);
}Screen Recording
function ScreenRecorder() {
const [stream, setStream] = useState(null);
const startScreenCapture = async () => {
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
video: { mediaSource: "screen" },
audio: true,
});
setStream(mediaStream);
};
const { startRecording, stopRecording, dataUrl } = useMediaRecorder(stream);
return (
<div>
<button onClick={startScreenCapture}>Share Screen</button>
{stream && (
<>
<button onClick={startRecording}>Start Recording</button>
<button onClick={stopRecording}>Stop Recording</button>
</>
)}
{dataUrl && (
<div>
<video src={dataUrl} controls style={{ maxWidth: "100%" }} />
<a href={dataUrl} download="screen-recording.webm">
Download
</a>
</div>
)}
</div>
);
}Features
- Audio/video recording - Record from any MediaStream source
- Pause/resume - Control recording with pause and resume
- Multiple sources - Works with microphone, camera, screen capture
- Automatic cleanup - Properly manages resources and memory
- Error handling - Comprehensive error handling
- TypeScript support - Full type definitions with generics
Browser Support
The MediaRecorder API is supported in:
- Chrome 47+
- Firefox 25+
- Safari 14.1+
- Edge 79+
- Opera 36+
Notes
- You must obtain a MediaStream before using the hook (via getUserMedia, getDisplayMedia, etc.)
- The recorded Blob is available after calling
stopRecording() dataUrlis automatically created and can be used for playback or download- Data URL is automatically revoked on unmount to prevent memory leaks
- Supported MIME types vary by browser - check
MediaRecorder.isTypeSupported() - The MediaRecorder is automatically stopped on component unmount
Parameters
| Argument | Type | Default | Description |
|---|---|---|---|
stream | MediaStream | null | required | Stream from getUserMedia, getDisplayMedia, or another source. |
options | object | {} | mimeType, audioBitsPerSecond, videoBitsPerSecond, and bitsPerSecond, passed to MediaRecorder. |
Behavior and lifecycle
When a supported, non-null stream is supplied, the effect constructs a MediaRecorder. Changing the stream/options or unmounting stops an active recorder. Each start clears prior chunks, blob, URL, and error. The stop event joins chunks into a Blob, creates an object URL, and sets state to "stopped"; replaced URLs are revoked. The hook stops the recorder, but it does not stop source media tracks—call track.stop() when capture is no longer needed.
Compatibility and accessibility
SSR reports isSupported: false. Creating the recorder and every control operation can set error; controls that do not match the current state are otherwise no-ops. Media capture requires HTTPS, permission, and a user gesture, and supported MIME types differ by browser—check MediaRecorder.isTypeSupported() before selecting one. Keep visible recording indicators and accessible start/stop controls, and honor revoked permission or ended tracks.