Capabilities · API
Document Picture-in-Picture API
Published
window.documentPictureInPicture.requestWindow() opens a same-origin, always-on-top window whose document you fill with arbitrary HTML, where the older Picture-in-Picture API accepts a single <video> element. The window floats above other windows, closes when the opener closes, cannot be navigated, and is limited to one per tab.
Chrome 116 and Edge 116 ship it on desktop only; BCD records version_added: false for Chrome on Android. Firefox 151 added it on desktop and also records no Android support. Safari has no implementation (BCD api.DocumentPictureInPicture).
Syntax
Section titled “Syntax”documentPictureInPicture.requestWindow()documentPictureInPicture.requestWindow(options)
documentPictureInPicture.windowdocumentPictureInPicture.addEventListener("enter", listener)requestWindow() returns a Promise<Window> that fulfils with the new window’s own Window object; its document starts as about:blank with the opener’s document base URL, so relative stylesheet and image URLs resolve against the opener. The window getter returns the last-opened Picture-in-Picture window while it is open and null otherwise. enter fires on documentPictureInPicture with a DocumentPictureInPictureEvent whose window property is the new window; it fires only for windows your own requestWindow() call opened.
Parameters
Section titled “Parameters”requestWindow() takes one optional options argument, a DocumentPictureInPictureOptions dictionary. Every member is optional.
| Member | Type | Required | Description |
|---|---|---|---|
width |
unsigned long long |
No | Requested viewport width in CSS pixels. 0 (the default) lets the browser choose. The browser may clamp a value that is too large or too small. |
height |
unsigned long long |
No | Requested viewport height in CSS pixels, same rules as width. Setting one of width and height without the other is a RangeError. |
disallowReturnToOpener |
boolean |
No | Default false. When true, a hint that the browser should not show the “back to tab” affordance on the window. Chrome 124. |
preferInitialWindowPlacement |
boolean |
No | Default false. When true, the browser should use the requested size and default position instead of restoring the size and position of the previously closed Picture-in-Picture window. Chrome 130. |
Both boolean members are hints; the specification uses “should” and “may”, so a browser that honours neither is conforming.
Exceptions
Section titled “Exceptions”requestWindow() rejects its promise in the following cases, listed in the order the specification checks them.
| Exception | Condition |
|---|---|
NotSupportedError |
The browser’s Document Picture-in-Picture support flag is false (for example the feature is disabled by policy). |
NotAllowedError |
The calling window is not a top-level traversable (an iframe); or the caller is itself a Picture-in-Picture window; or the window has no transient user activation. |
RangeError |
width is greater than zero but height is missing or zero, or the reverse. |
Opening a second window from the same tab is not an error: the specification closes the previous Picture-in-Picture window first and resolves with the new one.
Examples
Section titled “Examples”Every example checks "documentPictureInPicture" in window first and keeps the content in the main document when the check fails. The call to requestWindow() stays inside the click handler because it consumes the transient activation.
Popping a player out with its stylesheets, then docking it back
Section titled “Popping a player out with its stylesheets, then docking it back”The new window starts empty, so copy the opener’s stylesheets before moving nodes into it. Listening for pagehide on the Picture-in-Picture window returns the player to its original container when the user closes the window.
const mainContainer = document.querySelector("#player-slot");const player = document.querySelector("#player");const popOut = document.querySelector("#pop-out");
if (!("documentPictureInPicture" in window)) { popOut.hidden = true; // no pop-out on this browser; the player stays docked}
popOut.addEventListener("click", async () => { const pipWindow = await documentPictureInPicture.requestWindow({ width: 320, height: 180, });
for (const sheet of document.styleSheets) { try { const css = [...sheet.cssRules].map((rule) => rule.cssText).join(""); const style = document.createElement("style"); style.textContent = css; pipWindow.document.head.append(style); } catch { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = sheet.href; // cross-origin sheet: link it instead of reading it pipWindow.document.head.append(link); } }
pipWindow.document.body.append(player); pipWindow.addEventListener("pagehide", () => mainContainer.append(player));});Reading cssRules on a cross-origin stylesheet throws a SecurityError, which is why the catch branch links the sheet by URL instead.
Reusing the open window instead of opening a second one
Section titled “Reusing the open window instead of opening a second one”documentPictureInPicture.window tells you whether a window is already open. Reusing it avoids the close-and-reopen flicker the specification mandates when requestWindow() is called while a window exists.
async function getPipWindow(button) { if (!("documentPictureInPicture" in window)) return null;
const existing = documentPictureInPicture.window; if (existing) return existing;
try { return await documentPictureInPicture.requestWindow({ disallowReturnToOpener: true, }); } catch (err) { console.error(`${err.name}: ${err.message}`); return null; }}
button.addEventListener("click", async () => { const pipWindow = await getPipWindow(button); if (!pipWindow) { button.textContent = "Pop-out unavailable"; return; } pipWindow.document.body.textContent = `Opened at ${new Date().toLocaleTimeString()}`;});Returning null from the helper lets the caller render an in-page state instead of an unhandled rejection.
See also
Section titled “See also”- Media Session API, whose
enterpictureinpictureaction handler is the only way to open the window without a click - Window Management API, for placing ordinary windows across screens
- Screen Wake Lock API, for keeping the screen on while a floating player runs
- Document Picture-in-Picture: requestWindow() method (wicg.github.io)
- Document Picture-in-Picture API (developer.chrome.com)
Specifications
| Specification | Status |
|---|---|
| Document Picture-in-Picture: requestWindow() method | WICG draft |
| Document Picture-in-Picture: DocumentPictureInPictureOptions dictionary | WICG draft |