Picture in picture
A picture-in-picture button and commands.
import { pip } from "react-video-kit/pip";Adds 680 B to a player1.8 KB on its ownSource
Adds a picture-in-picture button to the controls, so the video can keep playing in a small window. Press the button below and the label in the corner changes to active. Close the window and it follows that too. The hotkeys plugin adds P.
Supported: no. Active: no.
"use client";import { useVideoState } from "react-video-kit";import { pip } from "react-video-kit/pip";import { VideoPlayer } from "react-video-kit/player";const plugins = [pip()];function PipLabel() { const supported = useVideoState(pip, (state) => state.supported); const active = useVideoState(pip, (state) => state.active); return ( <p className="pointer-events-none absolute top-3 left-1/2 -translate-x-1/2 rounded-full bg-black/45 px-3 py-1 text-xs whitespace-nowrap text-white backdrop-blur-md"> Supported: {supported ? "yes" : "no"}. Active: {active ? "yes" : "no"}. </p> );}export default function Pip() { return ( <VideoPlayer src="/media/sample-720.mp4" poster="/media/poster.jpg" plugins={plugins} load="visible"> <PipLabel /> </VideoPlayer> );}Usage
import { pip } from "react-video-kit/pip";import { VideoPlayer } from "react-video-kit/player";const plugins = [pip()];<VideoPlayer src="/video.mp4" plugins={plugins} />;When the button shows
The button renders nothing while supported is false. The plugin sets it when the video attaches. It's true if the video doesn't have disablePictureInPicture set, and either of these is:
document.pictureInPictureEnabledis true.video.webkitSupportsPresentationMode("picture-in-picture")returns true. This is Safari's own API.
supported is false on the server and until the video attaches, so the button isn't in the server-rendered HTML. The plugin doesn't check again later, so setting disablePictureInPicture after that has no effect on the button.
To hide the button for one player, set the attribute through media:
<VideoPlayer src="/video.mp4" plugins={plugins} media={{ disablePictureInPicture: true }} />;Build your own control
enter and toggle return a promise that rejects if the browser refuses, for example outside a click or key press. Catch that in your own control.
"use client";import { useCommands, useVideoState } from "react-video-kit";import { pip } from "react-video-kit/pip";function PipToggle() { const { toggle } = useCommands(pip); const active = useVideoState(pip, (state) => state.active); const supported = useVideoState(pip, (state) => state.supported); if (!supported) return null; return <button onClick={() => toggle().catch(() => {})}>{active ? "Bring back" : "Pop out"}</button>;}Pip is the button from the controls, which the plugin adds to the top-start slot. It catches a refusal itself, and nothing changes. In your own layout, render the slot or the button itself.
Behaviour
enteruses the standardrequestPictureInPicture()wheredocument.pictureInPictureEnabledis true, and falls back towebkitSetPresentationModeotherwise.activefollows the browser's events, so it also changes when the viewer closes the window.- The plugin sets
data-pipon the player's root whileactiveis true, for your own CSS. The default skin doesn't use it. - When the video detaches, such as when the player unmounts, an open window for that video closes.
API
Options
This plugin takes no options.
State
Read it with useVideoState(pip, (state) => …).
| Field | Type | Description |
|---|---|---|
| active | boolean | True while the video is in picture-in-picture. |
| supported | boolean | True when the browser allows picture-in-picture and the video doesn't turn it off. |
Commands
Call them with useCommands(pip).
| Command | Type | Description |
|---|---|---|
| enter | () => Promise<void> | Moves the video into picture-in-picture. |
| exit | () => Promise<void> | Brings the video back from picture-in-picture. |
| toggle | () => Promise<void> | Enters picture-in-picture, or exits it if already active. |
Parts
| Part | Description |
|---|---|
| Pip | Renders nothing where picture-in-picture isn't available. |