Choosing the Right Offscreen Reason

Pick the correct chrome.offscreen reason for each task: DOM_PARSER, CLIPBOARD, AUDIO_PLAYBACK, USER_MEDIA, WORKERS, LOCAL_STORAGE, MATCH_MEDIA, GEOLOCATION and more, with lifetime rules, combining reasons and review expectations.

Published October 2, 2026 Updated October 2, 2026 7 min read
Table of Contents

chrome.offscreen.createDocument requires a reasons array and a justification, and the choice is not cosmetic. Some reasons change the document’s lifetime — an AUDIO_PLAYBACK document is closed automatically after thirty seconds of silence — and all of them document to reviewers what the hidden page is for. Using DOM_SCRAPING because it sounds general, or TESTING to avoid thinking about it, invites review questions and surprises at runtime. This guide maps each common task to its reason and explains how reasons interact when one document does several jobs. It belongs to offscreen documents and DOM access.

What reasons are for

Offscreen documents exist to give MV3 extensions access to DOM-dependent web APIs that the service worker lacks, without bringing back permanent background pages. Reasons make that contract explicit: each reason names a capability the document needs, Chrome uses some of them to manage lifetime, and the Chrome Web Store expects the declared reasons to match what the document actually does. An extension may have only one offscreen document at a time, but that document may declare several reasons, so a single hidden page can parse HTML, access the clipboard and play sounds. Choosing reasons carefully keeps both behaviour and review predictable.

Offscreen reasons and the tasks they coverCommon chrome.offscreen reasons, the task each is for, and whether Chrome applies special lifetime handling.ReasonUse it forLifetime noteDOM_PARSERDOMParser on HTML/XMLNormalDOM_SCRAPINGScraping in an iframeNormalCLIPBOARDClipboard read/writeNormalAUDIO_PLAYBACKPlaying audioClosed after 30 s silenceUSER_MEDIA / DISPLAY_MEDIAMic, camera, screen captureNormalWORKERSSpawning web workersNormalLOCAL_STORAGElocalStorage migrationNormalMATCH_MEDIA / GEOLOCATIONmatchMedia, locationNormal
Pick the reason that names the capability you use — not the most general one.

Step-by-step: match tasks to reasons

1. Name the capability, not the feature

1// Parsing an RSS feed fetched by the worker
2await chrome.offscreen.createDocument({
3  url: "offscreen.html",
4  reasons: ["DOM_PARSER"],
5  justification: "Parse RSS XML with DOMParser",
6});

Execution context: the service worker. The question is “which web API does the document call that the worker cannot?” — DOMParser → DOM_PARSER; navigator.clipboard or execCommand('copy') → CLIPBOARD; new Audio() → AUDIO_PLAYBACK; getUserMedia → USER_MEDIA; new Worker() → WORKERS; matchMedia → MATCH_MEDIA; navigator.geolocation → GEOLOCATION; reading localStorage written by an MV2 background page → LOCAL_STORAGE. The justification should state that API and the feature it serves in one line.

2. Prefer worker-native APIs when they exist

1// No offscreen document needed for these in the service worker:
2await fetch(url);                                     // network
3new OffscreenCanvas(32, 32);                          // image work
4await createImageBitmap(blob);                        // decoding
5crypto.subtle.digest("SHA-256", data);                // hashing
6indexedDB.open("db");                                 // storage

Execution context: the service worker. Many tasks that needed a background page in MV2 work directly in the worker now. An offscreen document costs creation time, memory and a review question; use one only when the worker genuinely lacks the API.

Do I need an offscreen document?Decision tree: if the worker has the API, use it directly; if a DOM-only API is needed briefly, create a document with the matching reason and close it; if several DOM features are needed often, keep one document with several reasons.Does the service worker have the API?yesUse it in the workerfetch, OffscreenCanvas, IDBNo documentno review questionno, occasionallyCreate, use, closeone matching reasonClose when donefree memoryno, frequentlyOne shared documentseveral reasonsClose when idletimer
The cheapest offscreen document is the one you don't create.

3. Combine reasons in one shared document

 1async function ensureOffscreen(reasonsNeeded) {
 2  const docs = await chrome.runtime.getContexts({ contextTypes: ["OFFSCREEN_DOCUMENT"] });
 3  if (docs.length) {
 4    const { offscreenReasons = [] } = await chrome.storage.session.get("offscreenReasons");
 5    if (reasonsNeeded.every((r) => offscreenReasons.includes(r))) return;
 6    await chrome.offscreen.closeDocument();                      // recreate with the union of reasons
 7  }
 8  const { offscreenReasons = [] } = await chrome.storage.session.get("offscreenReasons");
 9  const reasons = [...new Set([...offscreenReasons, ...reasonsNeeded])];
10  await chrome.offscreen.createDocument({ url: "offscreen.html", reasons, justification: "Clipboard, HTML parsing and notification sounds" });
11  await chrome.storage.session.set({ offscreenReasons: reasons });
12}

Execution context: the service worker. Because only one document may exist, an extension using several DOM features needs either one document declaring all their reasons or a close-and-recreate step when a new reason is needed. Tracking declared reasons in session storage lets the worker decide. Note that combining AUDIO_PLAYBACK with other reasons does not exempt the document from audio’s automatic closing behaviour; design for the document disappearing.

4. Handle AUDIO_PLAYBACK’s automatic close

1// sw.js
2export async function playChime() {
3  await ensureOffscreen(["AUDIO_PLAYBACK"]);
4  await chrome.runtime.sendMessage({ target: "offscreen", type: "play", src: "sounds/chime.mp3" });
5}

Execution context: the service worker. Chrome closes a document whose only reason is audio playback after it has been silent for about thirty seconds. Always call ensureOffscreen before each use rather than assuming the document still exists. See playing audio from an extension.

An audio document closing itselfThe worker creates a document with AUDIO_PLAYBACK and plays a sound; after about thirty seconds of silence Chrome closes the document; the next play request finds no document and recreates it.Service workerChromeOffscreen doccreate(AUDIO_PLAYBACK) + playsilent for ~30 sclose documentgetContexts → nonecreate again + play
Check for the document before every use — it may be gone.

5. Avoid TESTING and vague reasons in production

TESTING exists for automated tests and should not appear in a published extension. DOM_SCRAPING is specifically for loading pages in an iframe to extract content — not a catch-all for “DOM stuff”. Reviewers compare declared reasons, the justification and the document’s code; a mismatch is an easy rejection.

6. Write justifications reviewers can verify

1Good:  "Parse the HTML of saved articles with DOMParser to extract the reading view."
2Good:  "Write the formatted citation to the clipboard when the user clicks Copy."
3Weak:  "Needed for functionality."
4Weak:  "Background processing."

Execution context: the justification string and your store listing. A concrete justification names the API and the user-visible feature. It also helps your future self understand why the document exists.

7. Close documents you no longer need

Even documents that Chrome does not close automatically should be closed when idle. An offscreen document is a full renderer with memory and, for some reasons, ongoing resource use. A small idle timer in the worker — close after a minute without requests — keeps the extension light.

Common mistakes

  • Using an offscreen document for something the worker can do. Extra cost, extra review.
  • A vague or wrong reason. Review questions and unpredictable lifetime.
  • Assuming the document still exists. Audio documents close themselves; always check.
  • Creating a second document. It throws; reuse or recreate with combined reasons.
  • Leaving documents open indefinitely. Memory for nothing.

Cross-browser variation

  • Chrome / Edge: chrome.offscreen from Chrome 109; new reasons were added over later versions (for example GEOLOCATION) — check availability for your minimum version.
  • Firefox: no offscreen API; the event-page background has a DOM for these tasks.
  • Safari: no offscreen API; use extension pages while open, worker-native APIs, or the containing app.

Verification

  1. For each offscreen use, confirm the declared reasons match the APIs called in the document’s code.
  2. Play a sound, wait a minute, play again, and confirm the document is recreated without errors.
  3. Check chrome://inspect → Other for the offscreen document and confirm it closes when idle.
  4. Grep the production manifest and code for TESTING: none.

FAQ

Can the offscreen document be visible?

No. It has no UI and cannot be shown; use a popup, side panel or tab for UI.

Which extension APIs can the document use?

Only chrome.runtime for messaging. It communicates with the worker for anything else.

Does the reason affect permissions?

Some features still need their own permission — geolocation, clipboardWrite in some cases — regardless of the reason.

Can I change reasons without closing the document?

No. Reasons are fixed at creation. To add one, close the document and create it again with the combined list — which is why tracking the declared reasons in session storage is useful.

What happens if my document uses an API its reasons don’t name?

The API usually still works — reasons are not a sandbox — but the mismatch is a review risk and may break if Chrome begins enforcing reasons more strictly. Declare what you use.

Is there a cost to declaring many reasons?

Not at runtime, but each reason is a claim reviewers may check. Declare the ones the document actually needs, and document why in the justification.

Other MV3 Architecture & Extension Lifecycle Resources