On this page
Document Picture-in-Picture: Breaking Out of the Browser Tab
Render arbitrary HTML, Canvas, and React components in an always-on-top Picture-in-Picture window.
Summary: Historically, Picture-in-Picture (PiP) was strictly limited to
<video>elements. The Document Picture-in-Picture API introduces the ability to open an always-on-top browser window that can render arbitrary HTML. This allows developers to build persistent pop-out widgets—like financial tickers, pomodoro timers, or video conferencing controls—without requiring a full desktop wrapper.
At a glance
| Property | Answer |
|---|---|
| API | Document Picture-in-Picture |
| Spec | Document Picture-in-Picture Specification |
| Availability | Limited / not Baseline |
| Desktop support | Chrome, Edge, Firefox, Opera |
| Safari | Not supported |
| Mobile | Not supported in major browsers |
| Secure context | Yes |
| User activation | Required for requestWindow() |
| Windows per tab | One |
| Content | Arbitrary HTML |
| Position control | Browser/user controlled |
| Main use | Persistent interactive web UI |
Who this is for: Frontend developers, UI platform architects, React developers, and teams building persistent web application interfaces. Reviewed against: Document Picture-in-Picture specification, MDN Web Docs, and current browser compatibility data. Last verified: August 2026.
1. What Document PiP is
For years, developers building real-time applications faced a severe limitation in the browser sandbox. If a user switched tabs or minimized the browser, the application’s UI vanished.
The Document Picture-in-Picture API solves this natively. It provides a real, interactive DOM inside an always-on-top floating window. Because it is a real DOM, you can append interactive buttons, complex CSS grids, and input fields. The user can click, type, and interact with it exactly like a normal web page.
Document Picture-in-Picture removes one important reason developers have historically reached for desktop wrappers: the need for an always-on-top interactive companion window. It does not replace the broader native capabilities provided by Electron (such as filesystem access or native process management).
Quick API Reference
| API | Purpose |
|---|---|
window.documentPictureInPicture | Access the API |
requestWindow() | Open a PiP window |
.window | Get the active PiP window |
enter | Detect successful opening |
resizeTo() | Set dimensions |
resizeBy() | Change dimensions |
display-mode: picture-in-picture | Style PiP-specific layout |
2. Document PiP vs Video PiP
The traditional Video PiP API (HTMLVideoElement.requestPictureInPicture()) hoists the video decoding layer out of the tab.
| Video PiP | Document PiP | |
|---|---|---|
| Content | <video> | Arbitrary HTML |
| DOM inside PiP | No | Yes |
| Custom HTML controls | No | Yes |
| CSS layout | No | Yes |
| Interactive inputs | No | Yes |
| Primary use | Media playback | Application UI |
Before Document Picture-in-Picture, developers who wanted interactive-looking content in a video PiP window could use workarounds such as rendering UI into a canvas and feeding that canvas into a video element. These approaches turned the UI into media rather than preserving a live DOM.
The Document PiP API (window.documentPictureInPicture.requestWindow()) returns a blank, fully functional Window object, allowing complete DOM manipulation. Use the standard <video> Picture-in-Picture API when your use case is limited to video and its browser support meets your requirements.
3. Browser support and feature detection
Document Picture-in-Picture has broad desktop-browser support but remains unavailable in Safari and major mobile browsers. Current MDN compatibility data confirms this distribution.
| Browser | Support |
|---|---|
| Chrome | 130+ |
| Edge | 130+ |
| Firefox | 151+ |
| Opera | 115+ |
| Safari | Not supported |
| Chrome Android | Not supported |
| Firefox Android | Not supported |
Before attempting to open a window, you must check for support using feature detection:
function supportsDocumentPiP() {
return "documentPictureInPicture" in window;
}
if (supportsDocumentPiP()) {
// Use Document PiP
} else {
// Fallback to in-page floating UI
}
4. The PiP window lifecycle
A browser tab can have at most one active Document Picture-in-Picture window at a time. The API provides a solid lifecycle for managing this single instance.
documentPictureInPicture.window
You can check if a window is already open by reading the window property. This prevents throwing errors if you attempt to open a duplicate instance.
if (window.documentPictureInPicture.window) {
console.log("A PiP window is already active!");
}
The enter event
The API provides an event-driven mechanism to detect when a window has successfully opened, which is useful if the window is requested from a deeply nested component but needs to be tracked globally.
window.documentPictureInPicture.addEventListener("enter", (event) => {
const activePipWindow = event.window;
console.log("PiP Window successfully opened.");
});
The pagehide event
When the user manually closes the PiP window, the window fires a pagehide event. Your application must listen to this to clean up resources or move DOM nodes back to the main document.
pipWindow.addEventListener("pagehide", () => {
// PiP closed by the user
console.log("PiP Window closed.");
});
5. Creating a window
Requesting a PiP window requires a user gesture (like a button click). It returns a promise that resolves with the new Window instance.
requestWindow() can reject with NotAllowedError for missing user activation or invalid calling context, NotSupportedError when the API is disabled, and RangeError for invalid dimensions. Production implementations must handle rejection gracefully.
async function openPipWidget() {
if (!("documentPictureInPicture" in window)) return;
if (window.documentPictureInPicture.window) return;
try {
const pipWindow = await window.documentPictureInPicture.requestWindow({
width: 320,
height: 480,
disallowReturnToOpener: false,
preferInitialWindowPlacement: true
});
// PiP window is ready to accept DOM nodes
} catch (error) {
console.error("Unable to open Document PiP:", error);
}
}
Options
| Option | Purpose |
|---|---|
width | Initial viewport width |
height | Initial viewport height |
disallowReturnToOpener | Hint to suppress the browser’s default “Back to tab” UI button |
preferInitialWindowPlacement | Prefer the originally requested size and position when reopening instead of restoring the user’s previous PiP placement |
6. Styling the separate document
The most critical nuance of this API is that the new window is completely blank. It does not automatically inherit the CSS stylesheets from your main tab.
The display-mode media query
In browsers that support the feature, the picture-in-picture display mode can be used to style the PiP document separately. This allows you to write specific rules for your widget when it is rendered inside the floating window, without needing JavaScript layout calculations.
@media (display-mode: picture-in-picture) {
body {
margin: 0;
overflow: hidden;
background-color: var(--pip-bg);
}
.hide-in-pip {
display: none;
}
}
Copying stylesheets
If you need your existing global styles to apply, you must inject them into the PiP document. The standard approach is to iterate through document.styleSheets and copy them to pipWindow.document.head.
Copying stylesheets is a snapshot operation. Applications that dynamically modify their styles (e.g., CSS-in-JS updates or responsive class changes) may need to synchronize those changes with the PiP document or use a shared stylesheet strategy where supported.
[...document.styleSheets].forEach((styleSheet) => {
try {
const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
const style = document.createElement('style');
style.textContent = cssRules;
pipWindow.document.head.appendChild(style);
} catch (e) {
// Accessing cssRules can throw a SecurityError when the stylesheet
// is not accessible to the document under the browser's CORS rules.
// The fallback works because the PiP document can load the stylesheet
// itself through a link tag; the opener does not need to read the rules.
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = styleSheet.href;
pipWindow.document.head.appendChild(link);
}
});
7. React and framework integration
The PiP window is a separate browsing context, but it is associated with the opening document and is same-origin with it. Code in the opener can directly access the PiP window’s DOM.
For frameworks like React, you should use a portal rather than physically moving DOM nodes. createPortal() lets React keep the component in the same React tree while mounting its DOM into pipWindow.document.body. React state, context, and event handling remain managed by the existing application rather than requiring a second React application inside the PiP window.
import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
export function PipTimer({ isPipActive, pipWindow }) {
const [seconds, setSeconds] = useState(0);
// The state interval runs in the main window
useEffect(() => {
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
const ui = (
<div className="bg-slate-900 text-white p-4">
<h2>Meeting Timer</h2>
<p>{seconds}s</p>
</div>
);
// Portal the UI to the external window if active
if (isPipActive && pipWindow) {
return createPortal(ui, pipWindow.document.body);
}
return ui; // Fallback to normal DOM rendering
}
In this React architecture, the main application remains the state owner while the PiP document functions as an alternate rendering surface.
React lifecycle
If the PiP window closes, the portal target (pipWindow.document.body) no longer exists. You need the parent application to clear the pipWindow state when the PiP window closes.
useEffect(() => {
if (!pipWindow) return;
const handlePageHide = () => setPipWindow(null);
pipWindow.addEventListener("pagehide", handlePageHide);
return () => pipWindow.removeEventListener("pagehide", handlePageHide);
}, [pipWindow]);
8. Window sizing and controls
Once the window is open, the browser exposes APIs to adjust its dimensions programmatically.
// Resize the window to absolute dimensions
pipWindow.resizeTo(400, 600);
// Increase the window width by 50px
pipWindow.resizeBy(50, 0);
In current Chrome implementations, both resizing methods require a user gesture. This is highly useful for expanding a collapsed widget (e.g., expanding a video conference call from a thumbnail to a grid view when someone shares their screen).
Position: The website cannot choose the PiP window’s screen coordinates. The browser and user control its placement.
9. Focus and returning to the main tab
The PiP window can programmatically return focus to the main tab, which is useful for “return to call” buttons or authentication flows. Focus control is constrained by the browser and generally requires a user gesture.
returnButton.addEventListener("click", () => {
window.focus(); // Returns focus to the opening tab
});
10. Security and permissions
The Document PiP API carries strict security requirements:
- Secure Context: The API is only available in secure contexts (HTTPS).
- Permissions Policy: The feature is controlled by the
picture-in-picturePermissions Policy directive. Administrators can explicitly disable it via HTTP headers:Permissions-Policy: picture-in-picture=(). - Window Chrome: The browser strictly controls the window chrome. You cannot hide the close button or masquerade as a native OS dialog.
- Navigation: The Document PiP window cannot be navigated to another URL; it remains associated with its opening document.
11. Accessibility considerations
Document PiP introduces complex window management scenarios that require explicit accessibility testing:
- Keyboard Navigation: Ensure focus is correctly managed when the PiP window opens, and that keyboard users can easily traverse the floating UI.
- Focus Restoration: When the PiP window closes, return focus to a logical element in the main tab rather than letting it reset to the document body.
- Announcements: Use ARIA live regions to announce to screen reader users that a new floating window has opened or that content has moved.
- Fallbacks: Always ensure the application remains fully usable if PiP is disabled by user preference or unsupported by the browser.
12. Decision framework
When designing a pop-out experience, evaluate your requirements:
- Do you only need to show a video? Use the standard
<video>PiP API when your use case is limited to video and its browser support meets your requirements. - Are you targeting mobile browsers? On unsupported mobile browsers, keep the UI inside the page or use a responsive bottom-sheet/floating panel rather than treating
window.open()as an equivalent replacement. - Do you need universal cross-browser desktop support today? If you need interactive floating UI in Safari,
window.open()can provide a separate browsing window where allowed, but it is not equivalent to Document PiP: browsers may block or constrain popups, and the site cannot force the window to remain always on top. - Do you need a rich, always-on-top widget in supported browsers? For financial tickers, call controls, or mini-dashboards, Document PiP provides the definitive modern architecture.
13. FAQ
Can Document PiP contain arbitrary HTML?
Yes. Unlike Video PiP, Document PiP provides a fully functional DOM that accepts complex HTML, CSS, interactive inputs, and canvas elements.
Can I use Document PiP with React?
Yes, using createPortal(). This allows the React tree in the main application to remain the state owner while mounting the DOM into the PiP document.
Does Document PiP work in Safari?
No, currently. It is only supported in modern Chromium browsers (Chrome, Edge, Opera) and Firefox 151+.
Can I have multiple PiP windows?
Only one per browser tab. Requesting a second window will reject the promise if one is already active for that document.
Can I control the PiP window position?
No. The browser and the operating system dictate where the window spawns, and the user can drag it around. You can only programmatically alter its dimensions via resizeBy or resizeTo.
Can I resize a Document PiP window?
Yes, subject to browser restrictions and user-gesture requirements.
Does Document PiP require HTTPS?
Yes, in supporting browsers. It is restricted to secure contexts.
Does Document PiP work in an iframe?
The API has strict top-level calling restrictions. requestWindow() can throw a NotAllowedError when called from a non-top-level window or a cross-origin iframe without explicit permissions policy delegation.
Can the PiP window access my site’s storage?
The PiP document is same-origin with the opening document, so normal same-origin access rules apply. Origin-scoped storage such as localStorage and IndexedDB can be used subject to browser storage rules. sessionStorage has additional browsing-context semantics, so applications should test the exact behavior they depend on rather than assuming it is identical to the opener’s session storage.
14. References
- W3C: Document Picture-in-Picture API Specification
- MDN: Document Picture-in-Picture API
- MDN: DocumentPictureInPicture: requestWindow()
- Chrome for Developers: Picture-in-Picture for any Element
- MDN: Permissions-Policy: picture-in-picture
From the team at
We build digital products and explore the modern web standards behind them.