Skip to content
Saurav-TB-Pandey edited this page Aug 10, 2026 · 2 revisions

usePip

A cutting-edge hook that leverages the new Document Picture-in-Picture API (different from the standard Video PIP API). It allows you to portal an entire React component—complete with interactivity and styles—into a floating window that stays on top of other applications even when the user switches browser tabs.

Warning

The Document PIP API is a modern browser feature (Chrome 116+). usePip gracefully degrades by setting isSupported: false on older browsers or Firefox/Safari.

Usage Examples

Basic

Opening a simple Picture-in-Picture window.

import { usePip } from "react-hook-lab";

function FloatingTimer() {
  const { isSupported, isOpen, openPip, closePip, Pip } = usePip();

  if (!isSupported) return <p>Your browser doesn't support Document PIP.</p>;

  return (
    <div>
      <button onClick={() => isOpen ? closePip() : openPip()}>
        {isOpen ? "Close PIP" : "Open Floating Timer"}
      </button>

      {/* The <Pip> component portals everything inside it into the floating window! */}
      <Pip width={300} height={200}>
        <div style={{ padding: 20, background: 'black', color: 'white' }}>
          <h2>10:00</h2>
          <button>Pause Timer</button>
        </div>
      </Pip>
    </div>
  );
}

Common

Using the <Pip> component to safely fallback. You can conditionally render the content in the main DOM if PIP is closed, or move it to the PIP window when opened.

import { usePip } from "react-hook-lab";

function VideoCall() {
  const { isOpen, openPip, closePip, Pip } = usePip();

  const VideoFeed = () => (
    <div className="video-feed">
      <video src="stream.mp4" autoPlay />
      <button>Mute</button>
    </div>
  );

  return (
    <div>
      <button onClick={() => isOpen ? closePip() : openPip()}>
        Toggle Pop-out
      </button>

      {/* If PIP is open, render inside it. Otherwise, render normally. */}
      {isOpen ? (
        <Pip width={400} height={300}>
          <VideoFeed />
        </Pip>
      ) : (
        <VideoFeed />
      )}
    </div>
  );
}

API Reference

Return Object

Returns an object of type UsePipResult containing:

  • isSupported (boolean): True if the browser supports window.documentPictureInPicture.
  • isOpen (boolean): True if the PIP window is currently active.
  • openPip ((options?: UsePipOptions) => Promise<Window | undefined>): Opens the PIP window. You can pass { width, height } as options.
  • closePip (() => void): Closes the active PIP window.
  • Pip (FC<UsePipOptions & { children: ReactNode }>): A React wrapper component that portals its children into the PIP window.

Core Working

Unlike the legacy <video>.requestPictureInPicture() which only floats a raw video stream, usePip uses the modern documentPictureInPicture API to create a literal second browser window.

  1. Initialization: When openPip() is called, the browser creates a new floating Window object.
  2. CSS Syncing: The hook immediately iterates through document.styleSheets in the main window and copies all CSS rules (or <link> tags) over to the new PIP window's <head>. This ensures your React component looks identical in the pop-out.
  3. Portaling: The <Pip> component returned by the hook is a wrapper around React's createPortal. When it renders, it mounts its children into a <div id="pip-root"> inside the new floating window. All React state, Contexts, and event listeners continue to function perfectly across the window boundary!

Clone this wiki locally