Skip to main content

Firefox 151 Adopts Document Picture-in-Picture API, Unlocking Always-on-Top Floating Web Content

Mozilla has officially shipped support for the Document Picture-in-Picture (DPIP) API in Firefox 151, expanding desktop browser capabilities beyond traditional media pop-outs. While standard Picture-in-Picture functionality has long allowed users to detach video elements into floating windows that remain visible across browser tabs and desktop workspaces, the Document Picture-in-Picture API removes the media constraint entirely. Developers can now render arbitrary web content—including full HTML structures, styled CSS components, and interactive JavaScript elements—inside an always-on-top window.

This evolution shifts the picture-in-picture concept from a specialized video player into a flexible framework for desktop web widgets. By leveraging the Document Picture-in-Picture API, web applications can create auxiliary floating tools that remain persistently accessible to users. Potential use cases include live stock market tickers, real-time customer support chats, audio playback control lists, floating interactive to-do lists, scratchpads, and dynamic data monitors.

The API operates by creating an entirely new document window context, referred to as a DPIP window, into which developers can inject or clone DOM nodes, stylesheets, and scripts from the parent application. While the concept is straightforward, implementing the API effectively requires careful handling of browser compatibility checks, DOM cloning strategies, window lifecycle controls, and targeted CSS adaptations to prevent contextual styling breakdowns.

The JavaScript of It All

Integrating the Document Picture-in-Picture API into a web application begins with feature detection. Because the API is strictly tailored for desktop environments and is not yet universally available across all major engines, applications must verify browser support before presenting pop-out controls to the user.

Ideally, developers would detect support for picture-in-picture display modes directly within CSS feature queries using the @supports rule paired with an @media test. However, modern CSS standards currently lack broad support for querying at-rules. Specifically, detecting media features via @supports at-rule(@media; display-mode: picture-in-picture) relies on the experimental at-rule() function. At present, full support for at-rule() remains limited primarily to Chromium-based browsers, and plans to support preludes such as (display-mode: picture-in-picture) within feature queries have largely stalled across vendors.

While recent developments—such as notes in Safari Technology Preview 251 detailing preliminary at-rule detection in @supports, and Firefox 155 subsequently announcing support for the feature—signal future progress, standard feature detection today must be handled entirely in JavaScript.

To safely implement the feature, developers inspect the global window object for the presence of the documentPictureInPicture property. If the property is absent, the interface can gracefully degrade by removing or disabling the corresponding user interaction element. When supported, an event listener can be attached to initiate the window creation sequence upon user request.

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 managing the lifecycle of a DPIP window, developers must consider user interaction patterns, particularly when a user triggers the pop-out control repeatedly. Opening a new DPIP window naturally replaces any active DPIP instance tied to the application. However, because opening a floating window automatically shifts keyboard and mouse focus to the new window, implementing a simple toggle button on the main page can occasionally lead to awkward focus state behavior.

If an application attempts to close an existing window programmatically using window.documentPictureInPicture.window.close(), the action may require multiple user clicks depending on where the focus currently resides. Furthermore, because native DPIP windows already include browser-provided close controls, duplicating close functionality inside the pop-out itself is often redundant. Allowing repeated clicks to recreate or re-initialize the window offers a practical alternative, as resetting the window can restore its initial dimensions and screen positioning if it has been moved or resized by the user.

Spawning the floating window is accomplished asynchronously via the requestWindow() method exposed on the window.documentPictureInPicture interface. This method accepts a configuration object that governs the dimensions and behavior of the detached document:

/* Create the DPIP window */
const DPIP = await window.documentPictureInPicture.requestWindow(
  width: 600,
  height: 400,
  preferInitialWindowPlacement: true
);

The width and height parameters explicitly define the initial pixel dimensions of the pop-out window. These properties must be supplied together; specifying only one dimension is invalid, though omitting both allows the browser to apply default dimensions.

The preferInitialWindowPlacement boolean option, when set to true, instructs the browser to ignore previously saved user position and sizing preferences for the window, ensuring it opens at its default state. Another supported parameter, disallowReturnToOpener, can be set to true to hide the native "Back to tab" button in the window header, which typically closes the pop-out while bringing the parent browser tab back into focus.

Because requestWindow() returns a Promise, application logic can execute asynchronously while the underlying window instance is constructed by the browser engine. Once resolved, the returned reference grants direct access to the child window’s complete DOM structure via DPIP.document.

Populating the newly created window involves populating its <body> and <head> elements with content from the main window. For single components—such as a stock ticker container—elements can be cloned directly using the standard DOM cloneNode(true) method and appended into the target document body:

/* 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));

However, transferring visual styling and assets requires a broader approach. Because the DPIP window functions as an independent document context, external stylesheets, inline <style> tags, and necessary runtime scripts must be explicitly copied into the floating window’s <head>.

To maintain rendering performance and avoid triggering multiple sequential layout reflows during initialization, developers should aggregate styling nodes into an off-screen document fragment using document.createDocumentFragment(). By appending all cloned <style> and <link rel="stylesheet"> elements to the fragment first, the entire batch of styles can be injected into the child document’s head in a single DOM operation.

/* 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);

Combining feature verification, window requesting, DOM cloning, and optimized style injection yields a functional JavaScript baseline for launching external desktop widgets:

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);
  );

It is worth noting that DPIP windows cannot be initialized within nested browsing contexts, such as cross-origin or sandboxed <iframe> elements. Interactive demonstrations hosted on platforms like CodePen must be executed in standalone or debug mode to function correctly.

Handling the CSS

Extracting a DOM node from its original document hierarchy and placing it inside a standalone floating window can alter CSS layout logic. In a main application view, a component may rely on parent wrapper dimensions, inherited typography rules, or high-specificity descendant selectors. When isolated inside a simplified DPIP window document, tight CSS coupling can cause rendering flaws unless styles are written to accommodate both normal and detached contexts.

To write CSS specifically tailored for components inside a floating window, developers must use the display-mode media feature. When an element is rendered inside a Document Picture-in-Picture window, the browser reports a display-mode value of picture-in-picture. This allows component stylesheets to apply layout overrides directly using standard media queries:

#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;
  

In this example, the #stock component adopts a self-contained inline width and rounded borders when rendered in the primary web layout. Once transferred to a picture-in-picture window, the nested media query expands the element to occupy the full width and height of the floating viewport while adjusting border radii to fit edge-to-edge against the window boundary.

A common point of confusion during implementation is the distinction between media queries and pseudo-classes. Web developers must note that the :picture-in-picture pseudo-class applies exclusively to standalone <video> elements managed by the traditional Picture-in-Picture API. It does not match generic HTML components or containers embedded within a Document Picture-in-Picture window. Target rules for arbitrary pop-out documents must rely entirely on the @media (display-mode: picture-in-picture) query.

Event Handling and Platform Adoption

The API also exposes dedicated event hooks on the global interface. Specifically, the enter event fires on the documentPictureInPicture object as soon as a floating document window is successfully opened:

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

Developers should distinguish this event from enterpictureinpicture, which is reserved for HTML video element transitions under the older media-centric specification. The enter event provides an execution point for logging telemetry, synchronizing application state, or attaching secondary event handlers to the child window instance.

With Firefox 151 joining Chrome in supporting the Document Picture-in-Picture API, desktop web developers gain a powerful standard for creating rich, detached interfaces. While complete cross-platform parity awaits broader adoption in engines like Apple’s Safari, the API’s graceful degradation path allows developers to build persistent, floating web widgets for supported desktop browsers today.

Layla Zulfa

Author at DesignEnt.

Flash News