⚠️ This package is currently in alpha and under active development. Please report any issues that you run across on either platform.
A React Native library powered by NitroModules that provides comprehensive screen recording capabilities for both iOS and Android. Capture in-app content or global screen recordings with camera overlay support, audio recording, and extensive customization options.
- In-App Recording (iOS only) - Record your app's content with camera overlay
- Global Screen Recording - System-wide screen capture (iOS & Android)
- Camera Integration - Front/back camera overlay with customizable positioning
- Audio Recording - Microphone support with permission management
- Event Listeners - Real-time recording status updates
- File Management - Automatic file handling and cache management
- Permission Management - Built-in camera and microphone permission handling
- React Hooks - Convenient hooks for permissions and global recording management
| iOS In-App Recording | iOS Global Recording | Android Global Recording |
|---|---|---|
![]() |
![]() |
![]() |
Using npm:
npm install react-native-nitro-screen-recorder react-native-nitro-modulesUsing yarn:
yarn add react-native-nitro-screen-recorder react-native-nitro-modules
react-native-nitro-modulesis required as this library relies on Nitro Modules.
This library includes an Expo config plugin for automatic native configuration.
Add the plugin to your app.config.js or app.json:
export default {
expo: {
plugins: [
[
'react-native-nitro-screen-recorder',
{
enableCameraPermission: true,
cameraPermissionText:
'Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay',
enableMicrophonePermission: true,
microphonePermissionText:
'Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio',
iosBroadcastExtensionTargetName: "ScreenRecorder",
iosAppGroupIdentifier:
'group.com.yourcompany.yourapp.screenrecording',
iosExtensionBundleIdentifier:
'com.yourcompany.yourapp.BroadcastExtension',
showPluginLogs: false,
},
],
],
},
};| Option | Type | Platform | Default | Description |
|---|---|---|---|---|
enableCameraPermission |
boolean |
iOS | true |
Whether to enable camera permission for screen recording with camera overlay |
cameraPermissionText |
string |
iOS | "Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay" |
Camera permission description text displayed in iOS permission dialog |
enableMicrophonePermission |
boolean |
iOS, Android | true |
Whether to enable microphone permission for screen recording with audio capture |
microphonePermissionText |
string |
iOS | "Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio" |
Microphone permission description text displayed in iOS permission dialog |
disableExperimental |
boolean |
iOS | false |
Whether to disable the experimental Expo appExtensions configuration. When true, skips applying the broadcast extension configuration |
iosBroadcastExtensionTargetName |
string |
iOS | BroadcastExtension |
The ability to customize the Target Name of the ios Broadcast Extension. |
iosExtensionBundleIdentifier |
string |
iOS | "${PRODUCT_BUNDLE_IDENTIFIER}.BroadcastExtension" |
The ability to customize the Broadcast Extension Bundle Identifier. |
iosAppGroupIdentifier |
string |
iOS | "group.${PRODUCT_BUNDLE_IDENTIFIER}.screen-recording" |
App Group identifier used to share data between the main app and its extensions |
showPluginLogs |
boolean |
iOS, Android | false |
Whether to display detailed plugin logs during the build process |
If you're using a bare React Native project (not using Expo), you'll need to manually configure the native iOS and Android projects.
Add the following permissions to your ios/YourApp/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your camera for screen recording with camera overlay</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your microphone for screen recording with audio</string>- Open your project in Xcode
- Select your main app target
- Go to Signing & Capabilities
- Click + Capability and add App Groups
- Create a new app group with identifier:
group.com.yourcompany.yourapp.screenrecording - Add the App Group identifier to your
Info.plist:
<key>AppGroupIdentifier</key>
<string>group.com.yourcompany.yourapp.screenrecording</string>- In Xcode, go to File → New → Target
- Choose Broadcast Upload Extension
- Name it
BroadcastExtension - Set the bundle identifier to
com.yourcompany.yourapp.BroadcastExtension
- Copy
SampleHandler.swiftfromnode_modules/react-native-nitro-screen-recorder/plugin/src/ios/SampleHandler.swiftto yourBroadcastExtension/folder - Copy
BroadcastWriter.swiftfromnode_modules/react-native-nitro-screen-recorder/plugin/src/ios/BroadcastWriter.swiftto yourBroadcastExtension/folder - Update the following values in
SampleHandler.swift:- Replace
<GROUPIDENTIFIER>with your app group identifier (e.g.,group.com.yourcompany.yourapp.screenrecording) - Replace
<SCHEME>with your app's custom URL scheme
- Replace
- Select the
BroadcastExtensiontarget in Xcode - Go to Signing & Capabilities
- Add App Groups capability
- Select the same app group you created earlier
- Set the Deployment Target to match your main app
- Ensure ReplayKit.framework is linked in Build Phases → Link Binary With Libraries
Update BroadcastExtension/Info.plist:
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.broadcast-services-upload</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).SampleHandler</string>
<key>RPBroadcastProcessMode</key>
<string>RPBroadcastProcessModeSampleBuffer</string>
</dict>
<key>AppGroupIdentifier</key>
<string>group.com.yourcompany.yourapp.screenrecording</string>Create BroadcastExtension/BroadcastExtension.entitlements:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.yourcompany.yourapp.screenrecording</string>
</array>
</dict>
</plist>Then in your extension target's Build Settings, set Code Signing Entitlements to BroadcastExtension/BroadcastExtension.entitlements.
Add the following permissions to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />Add the screen recording service to your AndroidManifest.xml inside the <application> tag:
<service
android:name="com.margelo.nitro.nitroscreenrecorder.ScreenRecordingService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />Add activity result handling to your MainActivity.java or MainActivity.kt:
import android.content.Intent;
import com.margelo.nitro.nitroscreenrecorder.NitroScreenRecorder;
import android.util.Log;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d("MainActivity", "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);
try {
NitroScreenRecorder.handleActivityResult(requestCode, resultCode, data);
} catch (Exception e) {
Log.e("MainActivity", "Error handling activity result: " + e.getMessage());
e.printStackTrace();
}
}import com.margelo.nitro.nitroscreenrecorder.NitroScreenRecorder
import android.content.Intent
import android.util.Log
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d("MainActivity", "onActivityResult: requestCode=$requestCode, resultCode=$resultCode")
try {
NitroScreenRecorder.handleActivityResult(requestCode, resultCode, data);
} catch (e: Exception) {
Log.e("MainActivity", "Error handling activity result: ${e.message}")
e.printStackTrace()
}
}- Replace
group.com.yourcompany.yourapp.screenrecordingwith your actual app group identifier - Replace
com.yourcompany.yourappwith your actual bundle identifier - Ensure both your main app and broadcast extension have the same App Group configured
- Test thoroughly on physical devices as screen recording doesn't work in simulators
- Make sure your app has a custom URL scheme configured for deep linking
After completing these steps:
- Build and run your app on a physical device
- Test global screen recording functionality
- Verify that recorded files are properly saved and accessible
- Check that permissions are properly requested when needed
Here's a complete example using the new useGlobalRecording hook and updated stopGlobalRecording:
import React from 'react';
import { View, Text, Button, Alert } from 'react-native';
import {
useGlobalRecording,
useMicrophonePermission,
startGlobalRecording,
stopGlobalRecording,
} from 'react-native-nitro-screen-recorder';
export default function ScreenRecorderExample() {
const { hasPermission, requestPermission } = useMicrophonePermission();
const { isRecording } = useGlobalRecording({
onRecordingStarted: () => {
Alert.alert('Recording started');
},
onRecordingFinished: async (file) => {
if (file) {
Alert.alert(
'Recording Complete!',
`Saved: ${file.name}\nDuration: ${file.duration}s\nSize: ${file.size} bytes`
);
// e.g., uploadRecording(file.path)
} else {
Alert.alert('Recording Complete', 'Failed to retrieve the file.');
}
},
settledTimeMs: 700, // optional delay before retrieving the file
});
const handleStartRecording = async () => {
if (!hasPermission) {
const granted = await requestPermission();
if (!granted) {
Alert.alert(
'Permission Required',
'Microphone permission is needed for audio recording'
);
return;
}
}
startGlobalRecording({
enableMic: true,
onRecordingError: (error) => {
Alert.alert('Global recording error', error.message);
},
});
};
const handleStopRecording = async () => {
const file = await stopGlobalRecording({ settledTimeMs: 1000 });
if (file) {
console.log('Stopped and retrieved file:', file);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Text style={{ fontSize: 18, marginBottom: 20, textAlign: 'center' }}>
Screen Recorder Demo
</Text>
<Button title="Start Global Recording" onPress={handleStartRecording} />
<Button title="Stop Recording" onPress={handleStopRecording} />
{isRecording && (
<Text style={{ marginTop: 10 }}>Recording is active…</Text>
)}
</View>
);
}Returns whether the user has granted permission to use the Camera, or not. If the user doesn't grant Camera Permission, you cannot use camera overlay features.
Platform: iOS, Android
Returns: Object with hasPermission boolean and requestPermission function
Example:
import { useCameraPermission } from 'react-native-nitro-screen-recorder';
const { hasPermission, requestPermission } = useCameraPermission();
if (!hasPermission) {
return <PermissionScreen onPress={requestPermission} />;
} else {
return <CameraRecordingScreen />;
}Returns whether the user has granted permission to use the Microphone, or not. If the user doesn't grant Audio Permission, you can still record but without audio.
Platform: iOS, Android
Returns: Object with hasPermission boolean and requestPermission function
Example:
import { useMicrophonePermission } from 'react-native-nitro-screen-recorder';
const { hasPermission: canRecordAudio, requestPermission } =
useMicrophonePermission();
// Use in recording configuration
const recordingOptions = {
enableMic: canRecordAudio,
enableCamera: true,
// ... other options
};React hook for monitoring and responding to global screen recording events.
Platform: iOS, Android
Parameters:
onRecordingStarted?: () => void— Called when a global recording begins.onRecordingFinished?: (file?: ScreenRecordingFile) => void— Called after recording ends (with a delay to allow the file to settle).onBroadcastPickerShown?: () => void— Called when the broadcast picker on ios is shown.onBroadcastPickerDismissed?: () => void— Called with the broadcast picker on ios is dismissed.ignoreRecordingsInitiatedElsewhere?: boolean -iOS-onlyallows the listener to only callback when thestartGlobalRecording` is called.settledTimeMs?: number— Milliseconds to wait after recording end before attempting to retrieve the file. Defaults to 500.
Returns: { isRecording: boolean } — whether a global recording is currently active.
Example:
import { useGlobalRecording } from 'react-native-nitro-screen-recorder';
const { isRecording } = useGlobalRecording({
onRecordingStarted: () => console.log('started'),
onRecordingFinished: (file) => {
if (file) {
console.log('finished:', file.path);
}
},
onBroadcastPickerShown: () => {
console.log('Perform some action');
},
onBroadcastPickerDismissed: () => {
console.log('Perform some other action');
},
ignoreRecordingsInitiatedElsewhere: false,
settledTimeMs: 600,
});Gets the current camera permission status without requesting permission.
Platform: iOS, Android
Returns: The current permission status for camera access
Example:
import { getCameraPermissionStatus } from 'react-native-nitro-screen-recorder';
const status = getCameraPermissionStatus();
if (status === 'granted') {
// Camera is available
}Gets the current microphone permission status without requesting permission.
Platform: iOS, Android
Returns: The current permission status for microphone access
Example:
import { getMicrophonePermissionStatus } from 'react-native-nitro-screen-recorder';
const status = getMicrophonePermissionStatus();
if (status === 'granted') {
// Microphone is available
}Requests camera permission from the user if not already granted. Shows the system permission dialog if permission hasn't been determined.
Platform: iOS, Android
Returns: Promise that resolves with the permission response
Example:
import { requestCameraPermission } from 'react-native-nitro-screen-recorder';
const response = await requestCameraPermission();
if (response.status === 'granted') {
// Permission granted, can use camera
}Requests microphone permission from the user if not already granted. Shows the system permission dialog if permission hasn't been determined.
Platform: iOS, Android
Returns: Promise that resolves with the permission response
Example:
import { requestMicrophonePermission } from 'react-native-nitro-screen-recorder';
const response = await requestMicrophonePermission();
if (response.status === 'granted') {
// Permission granted, can record audio
}Starts in-app screen recording with the specified configuration. Records only the current app's content, not system-wide screen content.
Platform: iOS only
Parameters:
enableMic: boolean - Whether to enable microphone audioenableCamera: boolean - Whether to enable camera overlaycameraPreviewStyle: RecorderCameraStyle - Camera positioning and stylingcameraDevice: CameraDevice - Front or back cameraonRecordingFinished: (file: ScreenRecordingFile) => void - Callback when recording completes
Example:
import { startInAppRecording } from 'react-native-nitro-screen-recorder';
await startInAppRecording({
enableMic: true,
enableCamera: true,
cameraPreviewStyle: { width: 100, height: 150, top: 30, left: 10 },
cameraDevice: 'front',
onRecordingFinished: (file) => {
console.log('Recording saved:', file.path);
},
});Stops the current in-app recording and returns the recorded video file. The recording file is also provided through the onRecordingFinished callback.
Platform: iOS only
Returns: Promise that resolves with the recording file or undefined if no recording was active
Example:
import { stopInAppRecording } from 'react-native-nitro-screen-recorder';
const file = await stopInAppRecording();
if (file) {
console.log('Recording stopped and saved:', file.path);
}Cancels the current in-app recording without saving the video. No file will be generated and onRecordingFinished will not be called.
Platform: iOS only
Example:
import { cancelInAppRecording } from 'react-native-nitro-screen-recorder';
await cancelInAppRecording(); // Recording discarded, no file savedStarts global screen recording that captures the entire device screen. Records system-wide content, including other apps and system UI.
Platform: iOS, Android
Parameters:
enableMic: boolean - Whether to enable microphone audioonRecordingError: (error: RecordingError) => void - Error callback
Throws:
Error: If microphone permission is not granted on Android whenenableMicistrue.
Example:
import { startGlobalRecording } from 'react-native-nitro-screen-recorder';
startGlobalRecording({
enableMic: true, // enableMic
onRecordingError: (error) => {
console.error('Global recording error:', error.message);
},
});Stops the current global screen recording and returns the saved video file. Because the system may take a short moment to finalize the asset writer output, you can pass an optional delay before retrieval.
Platform: iOS, Android
Parameters:
options.settledTimeMs?: number— Milliseconds to wait after the broadcast ends before attempting to retrieve the file. Defaults to 500.
Example:
import { stopGlobalRecording } from 'react-native-nitro-screen-recorder';
const file = await stopGlobalRecording({ settledTimeMs: 1000 });
if (file) {
console.log('Global recording saved:', file.path);
}Retrieves the most recently completed global recording file. Returns undefined if no global recording has been completed.
Platform: iOS, Android
Returns: The last global recording file or undefined if none exists
Example:
import { retrieveLastGlobalRecording } from 'react-native-nitro-screen-recorder';
const lastRecording = retrieveLastGlobalRecording();
if (lastRecording) {
console.log('Duration:', lastRecording.duration);
console.log('File size:', lastRecording.size);
}Adds a listener for screen recording events (began, ended, etc.). Returns a cleanup function to remove the listener when no longer needed.
Platform: iOS, Android
Parameters:
listener: Callback function that receives screen recording eventsignoreRecordingsInitiatedElsewhere?: boolean -iOS-onlyallows the listener to only callback when thestartGlobalRecording` is called.
Returns: Cleanup function to remove the listener
Example:
import { useEffect } from 'react';
import { addScreenRecordingListener } from 'react-native-nitro-screen-recorder';
useEffect(() => {
const removeListener = addScreenRecordingListener({
ignoreRecordingsInitiatedElsewhere: false,
listener: (event) => {
console.log('Event type:', event.type, 'Event reason:', event.reason);
},
});
// Clean up listener when component unmounts
return removeListener;
}, []);Adds a listener for iOS broadcast picker status changes (showing & dismissed). Returns a cleanup function to remove the listener when no longer needed. This helps when trying to perform some action on iOS as soon as the broadcast picker is dismissed.
Platform: iOS only (returns no-op cleanup function on Android)
Parameters:
listener: Callback function that receives broadcast picker presentation events
Returns: Cleanup function to remove the listener
Example:
import { useEffect } from 'react';
import { addBroadcastPickerListener } from 'react-native-nitro-screen-recorder';
useEffect(() => {
const removeListener = addBroadcastPickerListener((event) => {
console.log('Picker status:', event);
switch (event) {
case 'showing':
console.log('Broadcast picker is showing');
break;
case 'dismissed':
console.log(
'Broadcast picker was dismissed without starting recording'
);
break;
}
});
// Clean up listener when component unmounts
return removeListener;
}, []);Event Types:
showing: The broadcast picker modal is displayed to the userdismissed: The broadcast modal was dismissed without starting recording
Clears all cached recording files to free up storage space. This will delete temporary files but not files that have been explicitly saved.
Platform: iOS, Android
Example:
import { clearRecordingCache } from 'react-native-nitro-screen-recorder';
clearRecordingCache(); // Frees up storage by removing temporary recording filesThe library exports comprehensive TypeScript types for all functionality:
// Permission types
export type PermissionStatus = 'denied' | 'granted' | 'undetermined';
export type PermissionResponse = {
canAskAgain: boolean;
granted: boolean;
status: PermissionStatus;
expiresAt: never | number;
};
// Hook types
export interface PermissionState {
hasPermission: boolean;
requestPermission: () => Promise<boolean>;
}
export type GlobalRecordingHookInput = {
onRecordingStarted?: () => void;
onRecordingFinished?: (file?: ScreenRecordingFile) => void;
settledTimeMs?: number;
};
export type GlobalRecordingHookOutput = {
isRecording: boolean;
};
// Recording configuration
export type RecorderCameraStyle = {
top?: number;
left?: number;
width?: number;
height?: number;
borderRadius?: number;
borderWidth?: number;
};
export type CameraDevice = 'front' | 'back';
// Recording file information
export interface ScreenRecordingFile {
path: string;
name: string;
size: number;
duration: number;
enabledMicrophone: boolean;
}
// Event types
export interface ScreenRecordingEvent {
type: 'global' | 'withinApp';
reason: 'began' | 'ended';
}
export interface RecordingError {
name: string;
message: string;
}- In-App Recording: Full support with camera overlay
- Global Recording: Full programmatic control including start and stop functionality
- Permissions: Camera and microphone permissions handled automatically
- App Extensions: Uses broadcast extensions for global recording
- In-App Recording: Not supported (use global recording instead)
- Global Recording: Full programmatic control including stop functionality
- Permissions: Microphone permission required for audio recording
- Media Projection: Uses Android's MediaProjection API
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library



