Record a view — or the whole app window — into an MP4, animations and all. Made for saving animated content (stories, stickers, Skia drawings, charts) as video.
- H.264 MP4 without sound, up to 1080 px wide by default, at up to 30 fps.
- Records what's on screen: native views, Skia and other GPU-drawn views, animations from any thread.
- iOS and Android, no permissions needed.
- React Native with the New Architecture (a TurboModule; tested with React Native 0.86 and Expo SDK 57).
- iOS 15.1 or newer.
- Android 8.0 (API 26) or newer to record; on older versions the app still runs and recording rejects with
E_UNSUPPORTED.
npx expo install react-native-screen-record
# or
npm install react-native-screen-recordThen rebuild the app (npx expo run:ios / run:android, or a new development build): the library has native code, so
it doesn't run in Expo Go. No config plugin is needed. In a React Native app without Expo, run pod install in ios/.
import { useRef, type ComponentRef } from 'react';
import { View } from 'react-native';
import { recordView } from 'react-native-screen-record';
function Story() {
const ref = useRef<ComponentRef<typeof View>>(null);
async function save() {
const uri = await recordView(ref, { durationMs: 5000 });
// `uri` is a temporary file:// URI: move it to keep it, share it or save it to the gallery.
}
return (
<View ref={ref} collapsable={false}>
{/* … */}
</View>
);
}collapsable={false} keeps a plain View from being flattened away on Android, so there's a native view to record.
To record until you decide to stop, pass durationMs: 0 and call stopRecording():
import { recordScreen, stopRecording } from 'react-native-screen-record';
const video = recordScreen({ durationMs: 0 });
// … later
stopRecording();
const uri = await video;Each recording is a new file in the app's temporary directory (iOS) or cache directory (Android), where the system may
delete it. Move it somewhere permanent to keep it (for example with expo-file-system), save it to the gallery (for
example with expo-media-library) or share it, and delete it once you're done: the library doesn't remove old
recordings.
Records the view target (a ref from useRef, or a native tag) and resolves to the video's file:// URI once it's
written. The view has to stay on screen while it's recorded.
Records the whole app window.
Ends the recording in progress early; its promise resolves with what was recorded until then. With durationMs: 0,
this is how a recording ends.
Whether a recording is in progress: from the call to recordView or recordScreen until its promise settles.
| Option | Default | Description |
|---|---|---|
durationMs |
5000 |
How long to record. 0 records until stopRecording(). |
width |
1080 |
Width of the video in pixels; the height keeps the proportions. |
fps |
30 |
Frames per second, at most. |
bitRate |
10_000_000 |
Average bit rate in bits per second. |
The promise rejects with an error whose code is one of:
| Code | When |
|---|---|
E_BUSY |
A recording is already in progress: one runs at a time. |
E_NO_VIEW |
The view isn't there (unmounted, or flattened away on Android). |
E_RECORDING |
Recording failed: the view has no size, no frame was recorded, etc. |
E_UNSUPPORTED |
Android older than 8.0. |
If the ref is empty when recordView is called, it rejects without a code, before reaching the native side.
- iOS: a
CADisplayLinkticks with the screen, at mostfpstimes a second. Each tick, the view's hierarchy is drawn (drawViewHierarchyInRect:afterScreenUpdates:NO, which includes Metal-backed views; through its layer if it isn't on screen) into a pixel buffer of anAVAssetWriterand appended with the time since the recording started. - Android:
PixelCopycopies the part of the window the view covers, which is drawn onto the input surface of a hardware H.264 encoder (MediaCodec);MediaMuxerwrites the MP4. If no encoder takes the requested size, it falls back to 720 px wide.
Frames carry the time they were captured, so the video plays at the right speed even when a slow device captures fewer frames than asked.
- On Android, whatever is drawn on top of the view (toolbars, toasts, the keyboard) is recorded too, since it's copied from the screen. Hide it while recording.
- Rounded corners or other clipping by a parent show up as black on Android for the same reason.
- No audio.
- Drawing every frame takes time on the main thread: big views at a high resolution may record at less than
fps, especially on the iOS simulator.
MIT
Made with create-react-native-library