Skip to main content

Mozilla Ships Document Picture-in-Picture API in Firefox 151, Opening the Door to Arbitrary Web Widgets

The release of Firefox 151 marks a significant milestone for web applications with the official inclusion of the Document Picture-in-Picture API. While web browsers have offered standard Picture-in-Picture capabilities for several years, those earlier implementations were strictly limited to HTML <video> elements, detaching media players into floating, top-level windows that persist across desktop spaces and tab switches. The Document Picture-in-Picture (DPIP) API fundamentally expands this architecture by allowing developers to pop out any arbitrary HTML content into an always-on-top, floating window.

By moving beyond simple video controls, the Document Picture-in-Picture API enables web developers to transform web components into lightweight, persistent desktop widgets. Interactive elements such as real-time stock tickers, live customer support chats, audio playlists, task lists, scratchpads, and dynamic spreadsheets can now be detached from the main document viewport. Users can keep these focused interfaces visible continuously while navigating between different browser tabs or operating system windows.

The core mechanics of constructing a Document Picture-in-Picture window involve instantiating a new top-level browsing context and populating it with standard HTML, CSS, and JavaScript. While the high-level concept is straightforward, implementing interactive elements outside their original document hierarchy introduces specific technical considerations, particularly regarding feature detection, cross-browser support, asset cloning, and style scoping.

The JavaScript Implementation and Window Lifecycle

Deploying the Document Picture-in-Picture API requires careful handling of feature availability and window creation workflows. Because the feature is currently targeted at desktop environments and lacks universal browser availability, runtime detection is an essential first step before attempting to invoke pop-out behavior.

Ideally, web developers would rely on CSS feature queries to detect media query support before rendering styling rules or user interface controls. However, checking support for media features through CSS presents compatibility hurdles. Querying @media (display-mode: picture-in-picture) via the CSS @supports rule depends on the at-rule() function, an experimental mechanism whose prelude parsing support has faced inconsistent adoption across browser engines.

@supports at-rule(@media; display-mode: picture-in-picture) 
  /* DPIP supported */

Browser vendors are gradually shifting their support for CSS-based feature detection. Release notes for Safari Technology Preview 251 indicate progress toward at-rule detection within @supports, while Firefox 155 officially announced support for the functionality shortly after the publication of the Firefox 151 release sequence. Nevertheless, because relying purely on CSS feature queries remains unreliable across current stable browser releases—and with Safari yet to ship support for the overall Document Picture-in-Picture API—feature detection must be executed programmatically in JavaScript.

To gracefully handle environments where the API is unavailable, developers can check for the presence of documentPictureInPicture on the global window object:

if (!("documentPictureInPicture" in window)) 
  /* DPIP not supported (remove button) */
  document.querySelector("button").remove();
 else 
  /* DPIP supported (listen for button click) */
  document.querySelector("button").addEventListener("click", async () => 
    /* ... */
  );

When invoking the API, calling window.documentPictureInPicture.requestWindow() creates the floating window. This method returns a Promise that resolves with the window context of the newly created Picture-in-Picture document. The requestWindow() method accepts an options object to configure dimensions and placement behaviors:

  • width and height: Define the initial inner dimensions of the pop-out window in pixels. If one dimension is defined, both must be specified; omitting both defers the sizing decision to the user agent.
  • preferInitialWindowPlacement: A boolean parameter that, when set to true, instructs the browser not to reuse the saved screen coordinates or dimensions from a previously opened Picture-in-Picture instance, ensuring the window opens with its declared default properties.
  • disallowReturnToOpener: An optional boolean parameter that, when set to true, removes the browser’s built-in "Back to tab" UI button inside the window frame.

The API naturally handles window management by replacing an active Picture-in-Picture window whenever a new request is made. However, managing user interactions on the originating page requires considering window state and focus behavior. Inspecting window.documentPictureInPicture.window allows developers to determine whether a DPIP window is currently active.

document.querySelector("button").addEventListener("click", async () => 
  /* If the DPIP window is open, close it */
  if (window.documentPictureInPicture.window) 
    window.documentPictureInPicture.window.close();
  
);

When a DPIP window opens, operating system focus immediately transfers to the new floating interface. Because of this focus shift, using a single button on the primary page as a toggle switch can result in unexpected user friction, as returning focus to the primary window may require an extra click. Furthermore, native browser chrome automatically appends a window close action to the DPIP frame, making custom close controls within the parent page secondary.

Once the floating window context is resolved via requestWindow(), DOM elements from the originating document must be migrated or copied into the child window. In a typical component cloning pattern, such as transferring an active stock ticker element (#stock), the targeted node is duplicated and appended to the child window’s body.

To ensure the cloned element retains its styling and visual structure, any active embedded <style> blocks or external <link rel="stylesheet"> elements must also be replicated into the child document’s <head>. Appending DOM nodes individually can trigger multiple layout recalibrations and performance hits. To optimize rendering, developers can gather all active style sheets, clone them into an off-screen DocumentFragment, and attach the fragment to the child window’s head in a single operation.

The following script illustrates the complete JavaScript sequence for feature detection, window initialization, node cloning, and efficient style transfer:

if (!("documentPictureInPicture" in window)) 
  /* DPIP not supported (remove button) */
  document.querySelector("button").remove();
 else 
  /* DPIP supported (listen for button click) */
  document.querySelector("button").addEventListener("click", async () => 
    /* Create the DPIP window */
    const DPIP = await window.documentPictureInPicture.requestWindow(
      width: 600,
      height: 400,
      preferInitialWindowPlacement: true
    );

    /* Select the component */
    const stock = document.querySelector("#stock");

    /* Clone the component and append it to the DPIP <body> */
    DPIP.document.body.append(stock.cloneNode(true));

    /* Select all <style>s and <link rel=stylesheet>s */
    const styles = document.querySelectorAll("style, [rel=stylesheet]");

    /* Create a document fragment */
    const documentFragment = document.createDocumentFragment();

    /* Clone the styles and append them to the DPIP <head> */
    styles.forEach((element) =>
      documentFragment.append(element.cloneNode(true))
    );

    /* Append the document fragment to the DPIP <head> */
    DPIP.document.head.append(documentFragment);
  );

Developers testing this implementation should note that Picture-in-Picture contexts are blocked within restricted, nested browsing contexts, such as embedded third-party <iframe> elements commonly found in inline code playgrounds. Testing DPIP components typically requires running the code in a top-level, un-framed browsing context.

Handling the CSS and Contextual Scoping

Transporting an HTML component out of its parent DOM hierarchy can break context-dependent CSS rules. Style rules reliant on ancestor selectors, specific ID containers, or strict layout cascades on the main page will fail to apply once the element resides within the isolated root of a Document Picture-in-Picture document.

To tailor presentation specifically for the detached window without maintaining entirely separate style sheets, developers can leverage the display-mode media feature. The @media (display-mode: picture-in-picture) query triggers specifically when a layout is rendered inside an active Picture-in-Picture window context.

For example, a stock ticker component might render as a fixed-width, rounded card within the primary page layout, but require fluid dimensions and altered border radii when occupying a floating window:

#stock 
  width: fit-content;
  border-radius: 0.7rem;

  @media (display-mode: picture-in-picture) 
    width: 100%;
    height: 100%;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  

A common point of confusion during styling is the distinction between media queries and pseudo-classes. The :picture-in-picture CSS pseudo-class is explicitly reserved for matching video elements currently displayed via the standard, video-only Picture-in-Picture API. It does not match the root elements or components embedded within a Document Picture-in-Picture window. Target rules for generic HTML widgets inside DPIP windows must rely on the @media (display-mode: picture-in-picture) media query instead.

Lifecycle Events and Implementation Outlook

The API exposes specialized event interfaces to monitor window instantiation and teardown states. The enter event fires on the global documentPictureInPicture object as soon as a floating document window is successfully opened and rendered:

documentPictureInPicture.addEventListener("enter", (event) => 
  /* DPIP window opened */
);

This event is distinct from the legacy enterpictureinpicture event, which remains tied specifically to HTMLVideoElement instances under the original video specification.

The addition of the Document Picture-in-Picture API in Firefox 151 aligns Mozilla’s rendering engine with Chromium-based browsers, expanding cross-platform support for modern web applications. By offering a standardized pathway to render arbitrary HTML structures in floating desktop windows, the API provides developers with a robust tool for building persistent, highly accessible web utility interfaces across operating systems.

Dwi Wanna

Author at DesignEnt.

Flash News