Quick answer
An offscreen document is a packaged hidden extension HTML page for a justified task that needs DOM APIs unavailable in a worker. Declare offscreen, provide the documented creation reason and a concrete justification, and communicate through runtime messages. It is not a replacement for a permanent background page.
Only the Runtime extension API is supported inside the offscreen document. Route storage or other privileged work through an appropriate extension context. If the user should inspect or edit the result, a visible extension page is often the better surface.
Practical implementation steps
- Identify the missing capability. Parsing HTML with a DOM parser can justify
DOM_PARSER; copying data to a clipboard is a different reason with its own requirements. Do not select a reason merely because it keeps a hidden page open longer. - Package a static HTML file and external scripts. It cannot be a remote page URL, and it cannot be focused. The general API is available from Chrome 109.
- Serialize creation and inspect existing contexts. An extension normally has only one open offscreen document; split incognito profiles have the documented separate-profile behavior.
runtime.getContextsrequires Chrome 116, so set that floor for the example below or implement a supported older-browser alternative. - Send a narrow target-labelled message and return only the computed data. The worker and other extension contexts can hear runtime messages, so unrelated listeners must not respond accidentally. Validate the task and payload size at both ends.
- Close the document after its owned tasks finish. If several jobs share it, use explicit job ownership or reference counting; one job must not close a document another job still needs. Audio playback has a specific idle closure rule; other reasons are not a promise of durable application state.
A LocaleStress-style HTML export can be generated as a string without a hidden DOM page. Use an offscreen document only if the actual export task needs a DOM capability. Product similarity does not establish that LocaleStress uses this API.
Implementation example
This worker helper creates one DOM-parser document and resets its in-flight promise on success or failure. It assumes Chrome 116+ and the offscreen permission.
let creating;
export async function ensureParserDocument() {
if (creating) return creating;
creating = (async () => {
const url = chrome.runtime.getURL("parser.html");
const contexts = await chrome.runtime.getContexts({
contextTypes: ["OFFSCREEN_DOCUMENT"], documentUrls: [url],
});
if (contexts.length) return;
await chrome.offscreen.createDocument({
url: "parser.html",
reasons: ["DOM_PARSER"],
justification: "Parse user-provided HTML into a bounded text-only summary",
});
})();
try { await creating; } finally { creating = undefined; }
}
The helper establishes a surface, not a complete safe parsing workflow. The receiver still needs bounded input, safe text extraction, and an acknowledged completion response. An existing document with a different path is a configuration conflict to resolve, not a reason to create a second one.
Tradeoffs and alternatives
A visible tab gives users context and control; an offscreen document avoids interrupting them for a brief mechanical task. Pure string processing belongs in the worker when it needs no DOM. Keep the least complex surface that can perform the job.
Common mistakes
- Using offscreen as a hidden permanent app server.
- Calling storage APIs directly inside it.
- Creating concurrent documents without coordination.
- Selecting an unrelated reason to avoid lifecycle behavior.
Production checklist
- A real DOM need and creation reason are documented.
- Browser minimum covers context inspection.
- Creation failure permits retry.
- Messages have target, validation, and completion semantics.
- Closure respects every active job.
FAQ
Can I load an arbitrary website as offscreen?
No. Its URL is a static HTML resource packaged with the extension.
Can an offscreen page keep my data safe from restart?
No. Durable state still needs explicit persistence and recovery.
What should I prove first?
That the task genuinely requires a DOM, then that two simultaneous requests share one document and a failed creation can be retried.
Primary sources
Related guides and examples
Need this implemented in a real product?
Browser tools with a clear purpose, focused permissions, and a maintainable Manifest V3 architecture.