import { RTCViewPipIOS } from "@stream-io/video-react-native-sdk";
<>
<RTCViewPipIOS />
<MyComponent />
</>;Picture in picture
Picture-in-picture (PiP) mode displays calls in a movable small window while using other apps.
iOS
Setup
CallContent activates PiP automatically when backgrounding during active calls. Disable with disablePictureInPicture prop.
For custom components, use RTCViewPipIOS:
Current user camera
iOS requires multitasking camera access for background camera use. Enabled automatically with voip in UIBackgroundModes (iOS 18+) or the multitasking-camera-access entitlement.
Enabling
Enable local camera in PiP:
CallContent: SetiOSPiPIncludeLocalParticipantVideototrueRTCViewPipIOS: SetincludeLocalParticipantVideototrue
Then apply native changes:
Update AppDelegate
Add header after #import "AppDelegate.h":
#import <WebRTCModuleOptions.h>Add to didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
options.enableMultitaskingCameraAccess = YES;
// the rest
}Add to app.json plugins:
{
"plugins": [
[
"@stream-io/video-react-native-sdk",
{
"iOSEnableMultitaskingCameraAccess": true,
}
],
]
}If Expo EAS build is not used, please do npx expo prebuild --clean to generate the native directories again after adding the config plugins.
Android
Setup
Changes to AndroidManifest
Add to MainActivity in AndroidManifest.xml:
<activity>
...
android:name=".MainActivity"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
...
</activity>Changes to MainActivity
Add imports:
import android.content.res.Configuration
import com.streamvideo.reactnative.StreamVideoReactNative
import android.os.Build
import androidx.lifecycle.Lifecycleimport android.content.res.Configuration;
import com.streamvideo.reactnative.StreamVideoReactNative;
import android.os.Build;
import androidx.lifecycle.Lifecycle;Add functions:
fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
if (isFinishing) {
return
}
if (lifecycle.currentState === Lifecycle.State.CREATED) {
// when user clicks on Close button of PIP
finishAndRemoveTask()
} else {
StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
}
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&
StreamVideoReactNative.canAutoEnterPictureInPictureMode) {
val config = resources.configuration
onPictureInPictureModeChanged(true, config)
}
}@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
if (isFinishing()) {
return;
}
if (getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
// when user clicks on Close button of PIP
finishAndRemoveTask();
} else {
StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
}
}
@Override
protected void onUserLeaveHint() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&
StreamVideoReactNative.Companion.getCanAutoEnterPictureInPictureMode()) {
Configuration config = getResources().getConfiguration();
onPictureInPictureModeChanged(true, config);
}
}Add to app.json plugins:
{
"plugins": [
[
"@stream-io/video-react-native-sdk",
{
"androidPictureInPicture": true,
}
],
]
}If Expo EAS build is not used, please do npx expo prebuild --clean to generate the native directories again after adding the config plugins.
Keep call alive in the background
PiP triggers background mode on Android. Add a foreground service to keep calls alive. See Keeping Call Alive.
Entering PiP mode
CallContent activates PiP on home button press. Disable with disablePictureInPicture prop.
For custom components, use useAutoEnterPiPEffect:
import { useAutoEnterPiPEffect } from "@stream-io/video-react-native-sdk";
useAutoEnterPiPEffect();Choosing what to render on PiP mode
PiP windows are small - render only essential content. CallContent automatically shows only the dominant speaker.
For custom rendering, use useIsInPiPMode:
import { useIsInPiPMode } from "@stream-io/video-react-native-sdk";
const isInPiPMode = useIsInPiPMode();Use this boolean to conditionally render content during PiP mode.