react-native-nitro-screen-recorder
⚠️ 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.
Features
- 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
Demo
iOS In-App Recording | iOS Global Recording | Android Global Recording |
---|---|---|
Installation
Using npm:
npm install react-native-nitro-screen-recorder react-native-nitro-modules
Using yarn:
yarn add react-native-nitro-screen-recorder react-native-nitro-modules
react-native-nitro-modules
is required as this library relies on Nitro Modules.
Configuration
This library includes an Expo config plugin for automatic native configuration.
Using Expo
</summary> Add the plugin to yourapp.config.js
or app.json
:
js
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,
},
],
],
},
};
#### Plugin Configuration Options
| 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 |
Using Bare Workflow (Non-Expo)
</summary> If you're using a bare React Native project (not using Expo), you'll need to manually configure the native iOS and Android projects. ## iOS Setup ### 1. Add Permissions to Info.plist Add the following permissions to yourios/YourApp/Info.plist
:
xml
<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>
### 2. Create App Group
1. Open your project in Xcode
2. Select your main app target
3. Go to Signing & Capabilities
4. Click + Capability and add App Groups
5. Create a new app group with identifier: group.com.yourcompany.yourapp.screenrecording
6. Add the App Group identifier to your Info.plist
:
xml
<key>AppGroupIdentifier</key>
<string>group.com.yourcompany.yourapp.screenrecording</string>
### 3. Create Broadcast Upload Extension
1. In Xcode, go to File → New → Target
2. Choose Broadcast Upload Extension
3. Name it BroadcastExtension
4. Set the bundle identifier to com.yourcompany.yourapp.BroadcastExtension
### 4. Configure Extension Files
1. Copy SampleHandler.swift
from node_modules/react-native-nitro-screen-recorder/plugin/src/ios/SampleHandler.swift
to your BroadcastExtension/
folder
2. Copy BroadcastWriter.swift
from node_modules/react-native-nitro-screen-recorder/plugin/src/ios/BroadcastWriter.swift
to your BroadcastExtension/
folder
3. 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
### 5. Configure Extension Settings
1. Select the BroadcastExtension
target in Xcode
2. Go to Signing & Capabilities
3. Add App Groups capability
4. Select the same app group you created earlier
5. Set the Deployment Target to match your main app
6. Ensure ReplayKit.framework is linked in Build Phases → Link Binary With Libraries
### 6. Update Extension Info.plist
Update BroadcastExtension/Info.plist
:
xml
<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>
### 7. Create Extension Entitlements
Create BroadcastExtension/BroadcastExtension.entitlements
:
xml
<?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
.
## Android Setup
### 1. Add Permissions to AndroidManifest.xml
Add the following permissions to android/app/src/main/AndroidManifest.xml
:
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" />
### 2. Add Service Declaration
Add the screen recording service to your AndroidManifest.xml
inside the <application>
tag:
xml
<service
android:name="com.margelo.nitro.nitroscreenrecorder.ScreenRecordingService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="mediaProjection" />
### 3. Update MainActivity
Add activity result handling to your MainActivity.java
or MainActivity.kt
:
#### For Java (MainActivity.java):
java
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();
}
}
#### For Kotlin (MainActivity.kt):
kotlin
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()
}
}
## Important Notes
- Replace group.com.yourcompany.yourapp.screenrecording
with your actual app group identifier
- Replace com.yourcompany.yourapp
with 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
Verification
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
Quick Start Example
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>
);
}
Documentation
Table of Contents
React Hooks
useCameraPermission(): PermissionState
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 />;
}
useMicrophonePermission(): PermissionState
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
};
useGlobalRecording(input): GlobalRecordingHookOutput
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 the
startGlobalRecording` 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,
});
Permissions
getCameraPermissionStatus(): PermissionStatus
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
}
getMicrophonePermissionStatus(): PermissionStatus
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
}
requestCameraPermission(): Promise<PermissionResponse>
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
}
requestMicrophonePermission(): Promise<PermissionResponse>
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
}
In-App Recording
startInAppRecording(input): Promise<void>
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);
},
});
stopInAppRecording(): Promise<ScreenRecordingFile | undefined>
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);
}
cancelInAppRecording(): Promise<void>
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 saved
Global Recording
startGlobalRecording(input): void
Starts 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 whenenableMic
istrue
.
Example:
import { startGlobalRecording } from 'react-native-nitro-screen-recorder';
startGlobalRecording({
enableMic: true, // enableMic
onRecordingError: (error) => {
console.error('Global recording error:', error.message);
},
});
stopGlobalRecording(options?): Promise<ScreenRecordingFile | undefined>
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);
}
retrieveLastGlobalRecording(): ScreenRecordingFile | undefined
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);
}
Event Listeners
addScreenRecordingListener(listener): () => void
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 the
startGlobalRecording` 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;
}, []);
addBroadcastPickerListener(listener): () => void
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
Utilities
clearRecordingCache(): void
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 files
Types
The 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;
}
Platform Differences
iOS
- 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
Android
- 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
Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
MIT
Made with create-react-native-library