npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-screen-record

v1.0.0

Published

Record a React Native view or the whole app window into an MP4 video, animations and all

Readme

react-native-screen-record

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.

Requirements

  • 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.

Installation

npx expo install react-native-screen-record
# or
npm install react-native-screen-record

Then 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/.

Usage

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;

Where the video goes

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.

API

recordView(target, options?): Promise<string>

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.

recordScreen(options?): Promise<string>

Records the whole app window.

stopRecording(): void

Ends the recording in progress early; its promise resolves with what was recorded until then. With durationMs: 0, this is how a recording ends.

isRecording(): boolean

Whether a recording is in progress: from the call to recordView or recordScreen until its promise settles.

Options

| 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. |

Errors

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.

How it works

  • iOS: a CADisplayLink ticks with the screen, at most fps times 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 an AVAssetWriter and appended with the time since the recording started.
  • Android: PixelCopy copies the part of the window the view covers, which is drawn onto the input surface of a hardware H.264 encoder (MediaCodec); MediaMuxer writes 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.

Things to know

  • 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.

Contributing

License

MIT


Made with create-react-native-library