How to Create Offscreen Documents in Chrome Extensions: A Complete Guide
Source: Dev.to
Introduction
If you’re building Chrome extensions with Manifest V3, you’ve likely encountered the challenge of service workers terminating frequently. Offscreen documents provide a hidden HTML environment that can maintain persistent state and perform continuous operations without being visible to the user.
What Is an Offscreen Document?
An offscreen document is a hidden HTML page that runs in the background of your extension. Unlike service workers, which are short‑lived to save resources, offscreen documents stay alive, allowing you to:
- Keep persistent data structures (e.g.,
Map,Set) - Perform heavy computations on large datasets
- Process audio/video streams that require continuous operation
Think of it as an invisible webpage that your extension controls, offering a stable environment for long‑running tasks.
Ideal Use Cases
- Persistent Data Processing – Maintain in‑memory data that would be lost when a service worker stops.
- Heavy Computations – Run CPU‑intensive algorithms without interruption.
- Audio/Video Processing – Handle media that needs ongoing access to memory.
Important Limitations
- Cannot access the host website’s DOM (no buttons, content, etc.).
- Available only in Manifest V3 extensions for Chrome (not supported in Firefox).
- Runs in an isolated context separate from content scripts.
- Can only use the
chrome.runtimeAPI for messaging; other APIs such asstorageortabsare unavailable.
Manifest Declaration
Add the offscreen permission to your manifest.json:
{
"manifest_version": 3,
"name": "Your Extension Name",
"version": "1.0.0",
"description": "A short description of your extension.",
"permissions": ["offscreen"],
"background": {
"service_worker": "background.js",
"type": "module"
}
}
Offscreen HTML File (offscreen.html)
Create this file in the extension root. It never appears to the user but provides the execution environment.
<!DOCTYPE html>
<html>
<head>
<title>Offscreen Document</title>
</head>
<body></body>
</html>
Offscreen Script (offscreen.js)
Handle persistent operations and listen for messages from the background script.
// Perform heavy processing or maintain persistent state
console.log('Offscreen document is live');
// Listen for messages from the background or content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'Blah_Blah') {
// Perform your processing here
// ...
// Example response
sendResponse({ result: 'processed' });
}
// Return true to keep the message channel open for async response
return true;
});
Note: Offscreen console logs appear in the same inspector as the service worker. Open
chrome://extensions/, enable Developer mode, and click the “service worker” link to view them.
Creating the Offscreen Document (background.js)
let creatingOffscreen = null;
async function ensureOffscreen() {
const offscreenUrl = chrome.runtime.getURL('offscreen.html');
// Check if an offscreen document already exists
if ('getContexts' in chrome.runtime) {
const contexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl],
});
if (contexts.length > 0) {
console.log('Offscreen document already exists');
return;
}
} else {
// Fallback for older Chrome versions
const clients = await self.clients.matchAll();
if (clients.some(client => client.url.includes(chrome.runtime.id))) {
return;
}
}
// Create the offscreen document if it doesn't exist
if (!creatingOffscreen) {
creatingOffscreen = chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['WORKERS'],
justification: 'Maintain persistent data structures for extension functionality',
});
await creatingOffscreen;
creatingOffscreen = null;
console.log('Offscreen document created successfully');
} else {
// Wait for an ongoing creation to finish
await creatingOffscreen;
}
}
// Ensure offscreen document on startup
chrome.runtime.onStartup.addListener(() => {
ensureOffscreen();
console.log('Extension started, offscreen document ensured');
});
// Ensure offscreen document on installation
chrome.runtime.onInstalled.addListener(() => {
ensureOffscreen();
console.log('Extension installed, offscreen document created');
});
// Handle explicit requests to ensure the offscreen document exists
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg && msg.type === 'ensure_offscreen') {
ensureOffscreen()
.then(() => sendResponse({ ok: true }))
.catch(err => sendResponse({ ok: false, error: String(err) }));
return true; // Keep channel open for async response
}
});
How ensureOffscreen Works
- Check existence – Uses
chrome.runtime.getContexts()(preferred) orself.clients.matchAll()(fallback). - Prevent duplicate creation – A shared
creatingOffscreenpromise avoids race conditions. - Create if needed – Calls
chrome.offscreen.createDocument()with the requiredurl,reasons, andjustification.
Offscreen Creation Parameters
url– Path to your offscreen HTML file.reasons– One or more valid reasons from the Chrome API (e.g.,WORKERS,AUDIO_PLAYBACK,DOM_PARSER, etc.).justification– Clear explanation for why the document is needed; this is reviewed during extension submission.
Closing Offscreen Documents
When the document is no longer required, you can close it to free resources:
async function closeOffscreen() {
try {
await chrome.offscreen.closeDocument();
console.log('Offscreen document closed');
} catch (error) {
console.error('Error closing offscreen document:', error);
}
}
Messaging Between Background and Offscreen
Use the Chrome runtime messaging API to communicate.
From background to offscreen:
chrome.runtime.sendMessage(
{ type: 'Blah_Blah' },
response => {
console.log('Response from offscreen:', response);
}
);
Browser Support & Characteristics
- Manifest version – Offscreen documents are only available in Manifest V3 (Chrome and other Chromium‑based browsers). Not supported in Firefox.
- DOM access – Cannot interact directly with the host page’s DOM; all data must be transferred via messaging.
- Persistence – State is retained between operations, unlike service workers.
- Visibility – Completely hidden from users; no UI elements are displayed.
Additional Resources
- Official Chrome documentation on offscreen documents.
- Feel free to explore the extFast boilerplate for a ready‑made extension setup that includes authentication, subscription handling, and more.