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

@management-and-computer-consultants/react-native-picture-in-picture

v1.0.0

Published

React Native Picture-in-Picture for keeping a session alive when the user presses Home. Android activity PiP + iOS AVKit sample-buffer PiP with a timer overlay.

Readme

react-native-picture-in-picture

React Native Picture-in-Picture for keeping a session alive when the user presses Home or switches apps (for example a voice recording).

npm: @management-and-computer-consultants/react-native-picture-in-picture
GitHub: Management-AND-Computer-Consultants/mcc-react-native-picture-in-picture
Native module: NativeModules.PictureInPicture

| Platform | Mechanism | |----------|-----------| | Android 8+ (API 26) | Activity PiP (enterPictureInPictureMode) | | Android 12+ (API 31) | setAutoEnterEnabled(true) on the Home gesture | | iOS 15+ | AVPictureInPictureController + hidden AVSampleBufferDisplayLayer |

The PiP window shows REC, a mm:ss countdown, and Playing / Paused. iOS PiP chrome play/pause is forwarded to JS.


Table of contents

  1. Install
  2. Architecture
  3. Package layout
  4. JS usage
  5. API
  6. Android host activity
  7. iOS host app
  8. Runtime flow
  9. Native method map
  10. Notes

1. Install

cd your-react-native-app
npm install @management-and-computer-consultants/react-native-picture-in-picture

package.json:

"@management-and-computer-consultants/react-native-picture-in-picture": "^1.0.0"

Then rebuild native (Metro reload is not enough):

npx react-native run-android
cd ios && pod install && cd .. && npx react-native run-ios

The package autolinks PictureInPicturePackage. Do not add it by hand in MainApplication unless autolinking is disabled.

Local checkout for edits: D:\CoDe\RNPlugin\react-native-picture-in-picture.


2. Architecture

┌─────────────────────────────────────────────────────────────────┐
│ Host JS                                                          │
│  enablePictureInPicture() / disablePictureInPicture()            │
│  updatePictureInPictureStatus(seconds, paused)                 │
│  subscribePictureInPicture({ onActiveChange, onPlayPause })  │
└───────────────────────────────┬─────────────────────────────────┘
                                │ NativeModules.PictureInPicture
                                │ events: PictureInPictureChange
                                │          PictureInPicturePlayPause (iOS)
┌───────────────────────────────▼─────────────────────────────────┐
│ Plugin (autolinked)                                             │
│  android: PictureInPicturePackage / Module / Host               │
│  ios: PictureInPicture.m (AVKit sample-buffer PiP)              │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Host app (required)                                             │
│  Android MainActivity → PictureInPictureHost lifecycle hooks     │
│  AndroidManifest supportsPictureInPicture                      │
│  iOS Info.plist UIBackgroundModes = audio                         │
└─────────────────────────────────────────────────────────────────┘

PiP is opt-in. Call enable only while the session is active. Call disable on unmount, or Home on later screens will try to enter PiP.

This plugin does not intercept the lock button. Pair with a wake-state listener if you need an unlock warning.


3. Package layout

react-native-picture-in-picture/
├── src/index.ts
├── android/
│   ├── build.gradle
│   └── src/main/java/com/reactnativepictureinpicture/
│       ├── PictureInPicturePackage.kt
│       ├── PictureInPictureModule.kt
│       └── PictureInPictureHost.kt      # call from MainActivity
├── ios/PictureInPicture.m
├── react-native.config.js
├── react-native-picture-in-picture.podspec
└── README.md

4. JS usage

import {
  disablePictureInPicture,
  enablePictureInPicture,
  subscribePictureInPicture,
  updatePictureInPictureStatus,
} from '@management-and-computer-consultants/react-native-picture-in-picture';

useEffect(() => {
  enablePictureInPicture();
  const unsubscribe = subscribePictureInPicture({
    onActiveChange: setIsPipActive,
    onPlayPause: playing => {
      // iOS PiP chrome — toggle pause/resume in your session
    },
  });
  return () => {
    unsubscribe();
    disablePictureInPicture();
  };
}, []);

useEffect(() => {
  updatePictureInPictureStatus(remainingSeconds, isPaused);
}, [remainingSeconds, isPaused]);

Default export:

import PictureInPicture from '@management-and-computer-consultants/react-native-picture-in-picture';

PictureInPicture.enable();
PictureInPicture.updateStatus(remainingSeconds, isPaused);

Recording-session aliases (enableRecordingPip, subscribeRecordingPip, …) match older Eyes & Ears helpers.


5. API

| Export | Description | |--------|-------------| | enablePictureInPicture() | Allow Home / recents to enter PiP | | disablePictureInPicture() | Stop auto-enter; iOS stops PiP | | enterPictureInPicture() | Enter now if enabled | | updatePictureInPictureStatus(seconds, paused) | Drive the overlay countdown | | subscribePictureInPicture({ onActiveChange, onPlayPause }) | Returns unsubscribe | | isPictureInPictureEnabled() | JS flag after enable / disable | | isPictureInPictureActive() | Last native active event | | isPictureInPictureSupported() | Promise<boolean> | | isNativePictureInPictureActive() | Promise<boolean> |

Aliases: enableRecordingPip, disableRecordingPip, enterRecordingPip, updateRecordingPipStatus, subscribeRecordingPip, isRecordingPipEnabled, isRecordingPipActive.

Events:

{ active: boolean }    // PictureInPictureChange
{ playing: boolean }   // PictureInPicturePlayPause (iOS chrome only)

6. Android host activity

Autolinking cannot override onUserLeaveHint / onPictureInPictureModeChanged. Wire the launch activity.

Manifest

android:supportsPictureInPicture="true"
android:resizeableActivity="true"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|density|fontScale|layoutDirection"

configChanges must include screenSize / smallestScreenSize so entering PiP does not recreate the activity.

MainActivity

import android.content.res.Configuration
import android.os.Bundle
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.reactnativepictureinpicture.PictureInPictureHost

class MainActivity : ReactActivity() {
  override fun getMainComponentName(): String = "YourApp"

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(null)
  }

  override fun onUserLeaveHint() {
    super.onUserLeaveHint()
    PictureInPictureHost.onUserLeaveHint(this)
  }

  override fun onPictureInPictureModeChanged(
      isInPictureInPictureMode: Boolean,
      newConfig: Configuration,
  ) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    PictureInPictureHost.onPictureInPictureModeChanged(this, isInPictureInPictureMode)
  }

  override fun onPictureInPictureRequested(): Boolean {
    if (PictureInPictureHost.onPictureInPictureRequested(this)) {
      return true
    }
    return super.onPictureInPictureRequested()
  }

  override fun onPause() {
    super.onPause()
    PictureInPictureHost.onPause(this)
  }

  override fun onResume() {
    super.onResume()
    PictureInPictureHost.onResume(this)
  }

  override fun createReactActivityDelegate(): ReactActivityDelegate =
      PictureInPictureHost.createReactActivityDelegate(this, mainComponentName)
}

Skipping React onPause while in PiP keeps JS (and a recorder) running.

If you previously had an in-app RecordingPipPackage, remove it from MainApplication so two modules do not fight.


7. iOS host app

  1. pod install after npm install (autolinks PictureInPicture.m).
  2. In Info.plist, keep audio in background modes (AVKit PiP expects it when audio is running):
<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

No extra Xcode Compile Sources step when using CocoaPods autolinking.


8. Runtime flow

Session starts
  enablePictureInPicture()
        │
User presses Home / switches apps
        │
        ├─ Android 12+ : system auto-enters PiP
        ├─ Android 8–11: onUserLeaveHint → enterPictureInPictureMode
        └─ iOS: WillResignActive → startPictureInPicture
        │
        ▼
PictureInPictureChange { active: true }
        │
updatePictureInPictureStatus(...) every tick
        │
User expands PiP / returns
        │
PictureInPictureChange { active: false }
        │
Session ends
  disablePictureInPicture()

9. Native method map

Same names on both platforms (NativeModules.PictureInPicture):

| JS | Android (PictureInPictureModule.kt) | iOS (PictureInPicture.m) | |----|----------------------------------------|------------------------------| | enable() | pipEnabled = true + ticker + applyParams | _pipEnabled = YES + setup + frame timer | | disable() | pipEnabled = false + hide overlay | stop PiP + stop frame timer | | enter() | enterIfEnabled(activity) | startPipIfPossible | | updateStatus(n, paused) | deadline from elapsedRealtime | _deadline from NSDate | | isSupported() | API 26 + feature flag | AVPictureInPictureController isPictureInPictureSupported | | isActive() | isInPictureInPictureMode | isPictureInPictureActive |

Host-only Android (PictureInPictureHost): onUserLeaveHint, onPictureInPictureModeChanged, onPictureInPictureRequested, onPause, onResume, createReactActivityDelegate.


10. Notes

  • Rebuild native after installing or changing this package. JS-only reload leaves NativeModules.PictureInPicture === undefined.
  • Do not register PictureInPicturePackage in MainApplication unless autolinking is off.
  • Enable only for an active session. Leaving pipEnabled true makes Home on later screens enter PiP.
  • Android 13+ skins may ignore auto-enter; onUserLeaveHint covers API 26–30.
  • PiP can be denied if the device has no FEATURE_PICTURE_IN_PICTURE.
  • This is not a lock-screen API. Lock still backgrounds the app.
  • iOS needs the hidden AVSampleBufferDisplayLayer; AVKit will not start PiP from a normal React view.

Checklist

  1. package.json depends on @management-and-computer-consultants/react-native-picture-in-picture.
  2. Android: supportsPictureInPicture + PictureInPictureHost in MainActivity.
  3. iOS: pod install + audio in UIBackgroundModes.
  4. JS: enable on session start, disable on unmount.
  5. subscribe for onActiveChange (and iOS onPlayPause if needed).
  6. updatePictureInPictureStatus whenever the countdown or pause flag changes.