react-native-screen-recorder-pro
v0.1.0
Published
React Native screen recorder package with Android WebM/MP4 and iOS ReplayKit MP4 support.
Maintainers
Readme
react-native-screen-recorder-pro
React Native native screen recorder package by aztrix-codes.
This package provides a native screen recorder for Android and iOS with chunked recording support. It is designed for assessment, proctoring, training, support-session, and long-running screen capture flows where recordings need to be uploaded in parts instead of waiting for one large final file.
Features
- Android MP4 recording using
MediaRecorder.OutputFormat.MPEG_4, H.264 video, and AAC audio. - Android WebM recording using
MediaRecorder.OutputFormat.WEBM, VP8 video, and Opus audio. - iOS MP4 recording using ReplayKit and
AVAssetWriter. - Chunked recording with configurable chunk duration.
- Native event listener for completed chunks.
- Recoverable chunk storage for supported recorders.
- Direct compatibility with native module names
ScreenRecorderNativeandWebmScreenRecorderNative. - TypeScript definitions included.
- React Native autolinking support for Android and iOS.
Supported Platforms And Formats
| Platform | Default format | Supported formats | Native module |
| --- | --- | --- | --- |
| Android | mp4 | mp4, webm | ScreenRecorderNative, WebmScreenRecorderNative |
| iOS | mp4 | mp4 | ScreenRecorderNative |
Android defaults to MP4 for broad upload/playback compatibility. WebM remains available on Android by explicitly passing format: "webm". iOS records MP4 because ReplayKit does not provide WebM output directly.
This package does not transcode between formats. If your backend requires WebM from iOS, upload the iOS MP4 as-is and transcode server-side.
Installation
npm install react-native-screen-recorder-proFor iOS, install pods after adding the package:
cd ios
pod installThen rebuild the app:
npx react-native run-android
npx react-native run-iosThis is a native module. It will not work in Expo Go unless you create a custom development build that includes the native code.
Android Setup
The package ships its own Android manifest entries for the foreground services:
com.aztrixcodes.screenrecorderpro.WebmScreenRecorderServicecom.aztrixcodes.screenrecorderpro.ScreenRecorderService
The package also declares these permissions:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />Your app is still responsible for runtime permission handling where required. At minimum, request microphone permission before starting a recording if you need audio.
On Android 13 and newer, request POST_NOTIFICATIONS if your app needs foreground service notifications to be visible to the user.
When recording starts, Android shows the system MediaProjection consent prompt. The user must approve this prompt before capture can begin.
iOS Setup
iOS uses ReplayKit. It records screen content into MP4 chunks.
Notes:
- Test on a real iOS device. Simulator support is limited by Apple APIs.
- iOS does not output WebM.
- The native module name is
ScreenRecorderNative. - ReplayKit may pause or interrupt capture when the app becomes inactive, protected content appears, the device locks, or iOS rejects capture.
Quick Start
import {
EVENTS,
addRecorderListener,
startRecording,
stopRecording,
} from "react-native-screen-recorder-pro";
const subscription = addRecorderListener(EVENTS.CHUNK_READY, chunk => {
console.log("Chunk ready:", chunk.path);
});
await startRecording({
chunkDurationMs: 10000,
recordingSessionId: "session-001",
});
await stopRecording();
subscription.remove();On Android and iOS, this starts MP4 recording by default.
The wrapper remembers the active format after startRecording, so stopRecording() can be called without passing the format for the same session. You can still pass the format explicitly when you prefer:
await stopRecording({ format: "mp4" });Use Case: Android MP4 Recording
import {
EVENTS,
addRecorderListener,
startRecording,
stopRecording,
} from "react-native-screen-recorder-pro";
const subscription = addRecorderListener(
EVENTS.CHUNK_READY,
async chunk => {
await uploadChunk({
uri: chunk.path,
durationMs: chunk.durationMs,
});
},
{ format: "mp4" }
);
await startRecording({
format: "mp4",
chunkDurationMs: 10000,
});
await stopRecording({ format: "mp4" });
subscription.remove();Android MP4 is the default. Use it when your upload or playback pipeline expects MP4/H.264. The Android MP4 event payload contains path and durationMs.
Use Case: Android WebM Recording
import {
EVENTS,
addRecorderListener,
startRecording,
stopRecording,
} from "react-native-screen-recorder-pro";
const subscription = addRecorderListener(
EVENTS.CHUNK_READY,
async chunk => {
await uploadChunk({
uri: chunk.localPath || chunk.path,
fileName: chunk.uploadFileName,
sequence: chunk.sequence,
isFinal: chunk.isFinal,
durationMs: chunk.durationMs,
});
},
{ format: "webm" }
);
await startRecording({
format: "webm",
chunkDurationMs: 10000,
recordingSessionId: "assessment-123",
uploadNameStrategy: "native_path",
nextSequenceNumber: 1,
});
await stopRecording({ format: "webm" });
subscription.remove();Android WebM uses MediaRecorder.OutputFormat.WEBM, VP8 video, and Opus audio. It includes richer recovery metadata with sequence numbers and upload file names.
Use WebM on Android when you want smaller chunk files or when your backend/player specifically supports WebM.
Use Case: iOS MP4 Recording
import {
EVENTS,
addRecorderListener,
startRecording,
stopRecording,
} from "react-native-screen-recorder-pro";
const subscription = addRecorderListener(EVENTS.CHUNK_READY, chunk => {
console.log("iOS MP4 chunk:", chunk.path);
});
await startRecording({
format: "mp4",
chunkDurationMs: 10000,
recordingSessionId: "ios-session-001",
});
await stopRecording({ format: "mp4" });
subscription.remove();iOS only supports mp4. Passing webm on iOS throws an unsupported format error.
Use Case: Resume Failed Uploads With Recovery
For WebM Android and iOS MP4 flows, completed chunks are persisted as recoverable records. This helps if the app closes, the network fails, or upload acknowledgement is delayed.
import {
acknowledgeRecoverableChunk,
getRecoverableChunks,
} from "react-native-screen-recorder-pro";
const sessionId = "assessment-123";
const chunks = await getRecoverableChunks(sessionId, { format: "webm" });
for (const chunk of chunks) {
await uploadChunk({
uri: chunk.localPath || chunk.path,
fileName: chunk.uploadFileName,
sequence: chunk.sequence,
});
await acknowledgeRecoverableChunk(sessionId, chunk.recoveryId, {
format: "webm",
});
}Call acknowledgeRecoverableChunk only after your backend confirms the chunk is safely stored.
To clear all recovery records for a session:
import { clearRecoveryState } from "react-native-screen-recorder-pro";
await clearRecoveryState("assessment-123", { format: "webm" });Use Case: React Hook Pattern
import { useEffect, useRef } from "react";
import {
EVENTS,
addRecorderListener,
startRecording,
stopRecording,
} from "react-native-screen-recorder-pro";
export function useScreenRecorder(sessionId) {
const subscriptionRef = useRef(null);
useEffect(() => {
subscriptionRef.current = addRecorderListener(EVENTS.CHUNK_READY, chunk => {
console.log("chunk ready", chunk);
});
return () => {
subscriptionRef.current?.remove();
subscriptionRef.current = null;
};
}, []);
async function start() {
await startRecording({
chunkDurationMs: 10000,
recordingSessionId: sessionId,
});
}
async function stop() {
await stopRecording();
}
return { start, stop };
}Always remove event subscriptions when the screen unmounts.
API Reference
startRecording(options)
Starts native screen recording.
startRecording(options?: StartRecordingOptions | number): Promise<unknown>Options:
| Field | Type | Default | Description |
| --- | --- | --- | --- |
| format | "webm" or "mp4" | Android: mp4, iOS: mp4 | Desired output format. Pass webm explicitly for Android WebM. |
| chunkDurationMs | number | 10000 | Target chunk duration in milliseconds. |
| recordingSessionId | string | "" | Stable ID used for recovery and upload grouping. |
| uploadNameStrategy | string | native_path | Controls generated upload file names where supported. |
| nextSequenceNumber | number | 1 | First sequence number for generated chunks. |
You can also pass a number directly:
await startRecording(10000);That is treated as chunkDurationMs.
stopRecording(options)
Stops native screen recording.
stopRecording(options?: { format?: "webm" | "mp4" }): Promise<unknown>The final chunk may be emitted through onChunkReady or returned in the native stop result depending on platform and format.
addRecorderListener(eventName, listener, options)
Subscribes to native recorder events.
addRecorderListener(
eventName: ScreenRecorderEventName,
listener: (event: unknown) => void,
options?: { format?: "webm" | "mp4" }
): EmitterSubscriptionRemove the listener when done:
const subscription = addRecorderListener(EVENTS.CHUNK_READY, onChunk);
subscription.remove();getRecoverableChunks(recordingSessionId, options)
Returns persisted chunk records for a session.
getRecoverableChunks(
recordingSessionId?: string,
options?: { format?: "webm" | "mp4" }
): Promise<RecoverableChunk[]>If the selected native module does not support recovery, the wrapper returns an empty array.
acknowledgeRecoverableChunk(recordingSessionId, recoveryId, options)
Removes one persisted recovery record after successful upload.
acknowledgeRecoverableChunk(
recordingSessionId?: string,
recoveryId?: string,
options?: { format?: "webm" | "mp4" }
): Promise<boolean>clearRecoveryState(recordingSessionId, options)
Clears all persisted recovery records for a session.
clearRecoveryState(
recordingSessionId?: string,
options?: { format?: "webm" | "mp4" }
): Promise<boolean>setAssessmentPortalActive(isActive, options)
Sets an iOS native flag used by the current native implementation for portal-style recording flows.
setAssessmentPortalActive(
isActive: boolean,
options?: { format?: "webm" | "mp4" }
): voidOn Android or unsupported modules this is a no-op.
getSupportedFormats(platform)
Returns supported formats for a platform.
getSupportedFormats("android"); // ["mp4", "webm"]
getSupportedFormats("ios"); // ["mp4"]getDefaultFormat(platform)
Returns the default format for a platform.
getDefaultFormat("android"); // "mp4"
getDefaultFormat("ios"); // "mp4"getNativeModule(format)
Returns the underlying native module for advanced use.
const module = getNativeModule("webm");Events
Import event names from EVENTS:
import { EVENTS } from "react-native-screen-recorder-pro";| Constant | Native event | Description |
| --- | --- | --- |
| EVENTS.CHUNK_READY | onChunkReady | A chunk has been finalized and is ready for upload. |
| EVENTS.RECORDING_INTERRUPTED | onRecordingInterrupted | iOS capture was interrupted. |
| EVENTS.RECORDING_RESUME_STARTED | onRecordingResumeStarted | iOS resume attempt started. |
| EVENTS.RECORDING_RESUMED | onRecordingResumed | iOS capture resumed. |
| EVENTS.RECORDING_RESUME_FAILED | onRecordingResumeFailed | iOS resume attempt failed. |
Typical chunk payload:
type RecoverableChunk = {
recoveryId?: string;
recordingSessionId?: string;
localPath?: string;
path?: string;
uploadFileName?: string;
sequence?: number;
durationMs?: number;
createdAt?: number;
isFinal?: boolean;
requiresWebm?: boolean;
};Notes:
- Use
localPath || pathas the upload URI. uploadFileNameis the recommended server-side file name when provided.sequenceis useful for ordering chunks.isFinalindicates the final emitted chunk for a recording.requiresWebmcan betruefor iOS recovery records because the local file is MP4 even if your upload pipeline expects WebM.
Direct NativeModules Compatibility
The package intentionally preserves these native module names:
import { NativeModules } from "react-native";
NativeModules.WebmScreenRecorderNative; // Android WebM
NativeModules.ScreenRecorderNative; // Android MP4 and iOS MP4This allows existing app code that already calls NativeModules directly to migrate later with minimal changes.
For new code, prefer the wrapper API exported from this package.
Choosing The Right Format
Use Android MP4 when:
- You want the default behavior.
- Your backend requires MP4/H.264.
- Your users need broad native playback compatibility.
Use Android WebM when:
- You explicitly pass
format: "webm". - You want smaller chunks.
- Your backend or player supports WebM.
- You need recovery metadata with sequence numbers and upload file names.
Use iOS MP4 when:
- You are recording on iOS. This is the only supported iOS output format.
Upload Flow Recommendation
Recommended production flow:
- Generate a stable
recordingSessionIdbefore starting. - Subscribe to
EVENTS.CHUNK_READY. - Start recording with
chunkDurationMs,recordingSessionId, andnextSequenceNumber. - Upload each chunk as soon as it is emitted.
- Acknowledge recoverable chunks only after successful backend confirmation.
- On app restart, call
getRecoverableChunks(recordingSessionId)and retry pending uploads. - Stop recording on screen exit, assessment submit, logout, or app-level cancellation.
- Remove listeners on unmount.
Error Handling
Common errors:
| Error | Cause | Fix |
| --- | --- | --- |
| Native module is not linked | App was not rebuilt after installing | Rebuild Android/iOS app. |
| Unsupported format | webm was requested on iOS or invalid format was passed | Use getSupportedFormats() before choosing format. |
| Activity is null | Android recording started before an Activity was available | Start only after the screen is mounted and focused. |
| User denied capture | Android MediaProjection consent was cancelled | Ask user to start recording again. |
| iOS interruption | ReplayKit stopped due to system/app state | Listen for interruption/resume events and handle gracefully. |
Limitations
- WebM is Android only.
- iOS output is MP4 only.
- The package does not provide video playback UI.
- The package does not upload files by itself.
- The package does not transcode files.
- Native screen recording requires a rebuilt native app.
- Browser/web React Native targets are not supported.
Publishing Checklist
Before publishing to npm:
cd react-native-screen-recorder-pro
npm pack --dry-run
npm publishThe package currently publishes as:
{
"name": "react-native-screen-recorder-pro",
"author": "aztrix-codes"
}License
MIT
