Android

Building Picture-in-Picture (PiP) Mode in React Native with Expo and TypeScript

June 1, 2026•4 min read•2 views
Android

StackQuill

Building Picture-in-Picture (PiP) Mode in React Native with Expo and TypeScript

Picture-in-Picture (PiP) mode is one of the most useful features for media-based mobile applications. It allows users to continue watching video content while navigating outside the app.

In this article, we will build a modern React Native PiP Mode Proof of Concept using:

  • React Native
  • Expo
  • TypeScript
  • react-native-video
  • Native Android PiP APIs

What is Picture-in-Picture (PiP)?

Picture-in-Picture mode allows a video player to continue playing inside a small floating window while users interact with other applications.

Popular apps using PiP:

  • YouTube
  • Google Meet
  • Netflix
  • WhatsApp Video Calls

Final Result

Features implemented:

  • Floating mini-player
  • Background playback
  • Auto-enter PiP mode
  • Manual PiP trigger
  • Modern UI
  • Reusable custom hook
  • Native Android integration

Source Code

The complete source code for this project is available on GitHub:

🔗 GitHub Repository: https://github.com/dainyjose/pip-video-player-mobile


Create Project

Expo Project Setup

bash
npx create-expo-app react-native-pip-poc

Move into the project:

bash
cd react-native-pip-poc

Install Dependencies

Install video package:

bash
npm install react-native-video

Project Structure

Instead of putting everything inside App.tsx, use a scalable architecture.

txt
src/
 ├── components/
 │    ├── MiniPlayer.tsx
 │    ├── PipButton.tsx
 │    ├── PipStatus.tsx
 │    └── VideoPlayer.tsx
 │
 ├── hooks/
 │    └── usePictureInPicture.ts
 │
 ├── constants/
 │    └── video.ts
 │
 ├── screens/
 │    └── PlayerScreen.tsx
 │
 └── types/
      └── video.ts

This keeps the code reusable and production-ready.


Android PiP Configuration

AndroidManifest.xml

Open:

txt
android/app/src/main/AndroidManifest.xml

Add:

xml
<activity
  android:name=".MainActivity"
  android:supportsPictureInPicture="true"
  android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
  android:launchMode="singleTask"
  android:windowSoftInputMode="adjustResize"
  android:exported="true">

This enables native Android PiP support.


Configure MainActivity.kt

Open:

txt
android/app/src/main/java/.../MainActivity.kt

Add:

kotlin
override fun onUserLeaveHint() {
    super.onUserLeaveHint()

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        val params =
            PictureInPictureParams.Builder()
                .setAspectRatio(Rational(16, 9))
                .build()

        enterPictureInPictureMode(params)
    }
}

This automatically enters PiP mode when the app moves to the background.


Create Reusable PiP Hook

usePictureInPicture.ts

Instead of writing all logic directly inside the screen, create a reusable hook.

tsx
import { useCallback, useRef, useState } from "react";
import { Platform } from "react-native";
import { VideoRef } from "react-native-video";

export const usePictureInPicture = (
  videoRef: React.RefObject<VideoRef>,
) => {
  const loadedRef = useRef(false);
  const pendingPipRef = useRef(false);

  const [pipActive, setPipActive] = useState(false);
  const [paused, setPaused] = useState(true);

  const enterPip = useCallback(() => {
    const delays =
      Platform.OS === "ios"
        ? [250, 700, 1200]
        : [300];

    delays.forEach((delay) => {
      setTimeout(() => {
        videoRef.current?.enterPictureInPicture();
      }, delay);
    });
  }, []);

  const activateFloatingPlayer = () => {
    pendingPipRef.current = true;
    setPaused(false);

    if (loadedRef.current) {
      enterPip();
    }
  };

  return {
    paused,
    setPaused,
    pipActive,
    setPipActive,
    loadedRef,
    activateFloatingPlayer,
  };
};

Benefits:

  • reusable logic
  • cleaner screens
  • easier maintenance
  • scalable architecture

Build Video Player Component

VideoPlayer.tsx

tsx
import Video from "react-native-video";

<Video
  ref={videoRef}
  source={VIDEO_SOURCE}
  style={styles.video}
  resizeMode="contain"
  paused={paused}
  controls
  playInBackground
  playWhenInactive
  enterPictureInPictureOnLeave
  ignoreSilentSwitch="ignore"
/>

Important PiP props:

PropPurpose
playInBackgroundContinue playback in background
playWhenInactiveAllow inactive playback
enterPictureInPictureOnLeaveAuto-enter PiP

Auto PiP on Background

Inside the screen:

tsx
useEffect(() => {
  const sub = AppState.addEventListener(
    "change",
    (state) => {
      if (state === "background") {
        activateFloatingPlayer();
      }
    },
  );

  return () => sub.remove();
}, []);

This creates a YouTube-like experience.


Modern UI Layout

Instead of basic buttons, create:

  • Player card
  • Floating mini-player
  • Status cards
  • Dark streaming UI

Example:

tsx
<View style={styles.playerCard}>
  <VideoPlayer />
  <PipButton />
  <PipStatus />
</View>

Running the Project

Android

bash
npx expo run:android

Important Notes

Expo Go Limitation

Expo Go may not fully support native PiP functionality.

Use native builds instead.


Common Issues

PiP Not Starting

Check:

  • Android version >= 8
  • Manifest configuration
  • Native build usage
  • Video loaded state

App Crashes

Verify:

  • correct MainActivity.kt imports
  • react-native-video installation
  • Android SDK compatibility

Future Improvements

This PoC can be extended with:

  • Custom PiP controls
  • Live streaming
  • Video call PiP
  • Draggable mini-player
  • Media session controls
  • Netflix-style player UI

Conclusion

Picture-in-Picture mode significantly improves user experience for media applications.

With React Native, Expo, and native Android APIs, we can build scalable and modern PiP experiences similar to YouTube and Netflix.

This project demonstrates:

  • native Android PiP integration
  • modular React Native architecture
  • reusable hooks
  • background playback handling
  • scalable UI structure

You can now extend this implementation into:

  • OTT applications
  • Video conferencing apps
  • Live class platforms
  • Social media video apps
  • Streaming platforms

Related Articles