Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.
Run extension-wide logic in background contexts with clear support for both Manifest V2 (MV2) background scripts and Manifest V3 (MV3) service workers.Extension.js reads background entries from manifest.json and compiles them into dedicated background outputs. It applies browser-specific reload behavior during development.
Firefox: service worker translated to an event page
Firefox does not run MV3 background.service_worker (loading such an add-on fails with “background.service_worker is currently disabled. Add background.scripts.”). You don’t need to special-case it: for the Firefox build, Extension.js automatically translates a background.service_worker entry into a background.scripts event page pointing at the same compiled bundle. Author one background.service_worker and it loads on both Chromium and Firefox.
The translation runs in the other direction too. In a Chromium MV3 build, a background.scripts array becomes background.service_worker pointing at the compiled bundle.Chromium MV3 rejects the whole extension whenever background.scripts is present, even next to a valid service_worker. Extension.js therefore always removes the scripts key from the emitted Chromium MV3 manifest. When the source declares both fields, the existing service_worker entry wins.
From 4.1.20, Safari builds emit a non-persistent background page. Extension.js translates a background.service_worker entry into background: {scripts: [...]}, the same translation it runs for Firefox.The reason was measured. A Manifest V3 service worker never started on Safari 26.5.2, including for extensions built by Apple’s own converter. A background page ran immediately. WebKit deliberately prefers the page form (WebKit bug 270750).
One unguarded Chromium-only call at the top level of a background script
throws on Safari, and Safari then discards the background context. You get no
logs, no reload, and no error, so the extension looks dead. Guard the
call, or branch at build time.
// Throws on Safari and takes the whole background context with it.chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });// Safe on every target.chrome.sidePanel?.setPanelBehavior({ openPanelOnActionClick: true });
Safari builds from 4.1.20 also warn about the extension APIs that Safari lacks, which is the cheapest way to find this before you start debugging. See Building Safari extensions.
background.service_worker runs under the browser’s service-worker lifecycle, not as a long-running process.That means:
In-memory state can disappear between events.
Design long-running work around events, not process permanence.
Startup should stay small and predictable.
Your code should restore important state from storage or recompute it safely.
Use background.scripts only for MV2-style compatibility cases. For MV3-first extensions, treat the service worker as the central coordinator for browser API calls and cross-context communication.
Some files never appear as import statements but still must ship with the extension. Extension.js traces these into the output so the built extension behaves like the source loaded unpacked in the browser:
importScripts(...) dependencies of classic service workers. String-literal arguments are resolved recursively (a dependency can call importScripts itself) against the emitted worker location, so worker-relative URLs keep working even though the worker moves to background/service_worker.js. WebAssembly sibling files loaded by wasm-bindgen glue (*_bg.wasm) ship alongside their loader.
chrome.scripting.executeScript({files: [...]}) and insertCSS({files: [...]}) payloads. Files referenced only from files: arrays (not declared in manifest content_scripts) are copied verbatim at their extension-root paths.
A referenced file that does not exist in your project produces a build warning naming the missing path, instead of a silent runtime failure.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Extension.js","description":"Build browser extensions for Chrome, Edge, Firefox, and Safari with one modern workflow. Extension.js handles manifest compilation, browser-specific output, reload behavior, and packaging so you can focus on product features.","colors":{"primary":"#0e62f7","light":"#60a5fa","dark":"#0e62f7"},"logo":{"light":"https://mintcdn.com/extensionjs/OSsaYW6REekgcRaD/logo-light.png?fit=max\u0026auto=format\u0026n=OSsaYW6REekgcRaD\u0026q=85\u0026s=0eebd66d9a894dd11e16686f9ef4a875","dark":"https://mintcdn.com/extensionjs/OSsaYW6REekgcRaD/logo-dark.png?fit=max\u0026auto=format\u0026n=OSsaYW6REekgcRaD\u0026q=85\u0026s=5cf23e76dbd27b25aef263d7b407fea5"},"favicon":"https://mintcdn.com/extensionjs/OSsaYW6REekgcRaD/favicon.png?fit=max\u0026auto=format\u0026n=OSsaYW6REekgcRaD\u0026q=85\u0026s=19a6b751f0b381529fdb96230bbc2c94","appearance":{"default":"dark"},"navbar":{"links":[{"icon":"github","href":"https://github.com/extension-js/extension.js","label":"GitHub"},{"icon":"npm","href":"https://www.npmjs.com/package/extension","label":"npm"},{"icon":"puzzle-piece","href":"https://docs.extension.dev?utm_source=extension-js-org\u0026utm_medium=sponsor\u0026utm_campaign=navbar","label":"extension.dev"}]},"footer":{"socials":{"github":"https://github.com/extension-js/extension.js","discord":"https://discord.gg/BRqbPDfPxb","x":"https://twitter.com/extensionjs"}},"seo":{"metatags":{"og:site_name":"Extension.js","og:image":"https://media.extension.land/brand/extension-js/social/og.png","og:image:type":"image/png","og:image:alt":"Extension.js, the cross-browser extension framework","twitter:site":"@extensionjs","twitter:creator":"@extensionjs","twitter:card":"summary_large_image","twitter:image":"https://media.extension.land/brand/extension-js/social/og.png","twitter:image:alt":"Extension.js, the cross-browser extension framework","google-site-verification":"BLSk7tcWG_qVtuAAf4UJyUbPIGXfghiJcUaDP4aoGpM","msvalidate.01":"E718A8AA0D4E3ED605BFF1BA1C0E46A7","yandex-verification":"e2a6f3f5424c1722"},"indexing":"all"},"integrations":{"gtm":{"tagId":"GTM-NJD6VVD4"},"posthog":{"apiKey":"phc_Np5x3Jg3h2V7kTFtNch2uz6QBaWDycQpIidzX5PetaN"}},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","cursor","vscode","mcp"],"display":"header"},"styling":{"latex":false},"navigation":{"languages":[{"language":"en","default":true,"href":""},{"language":"zh-Hans","href":"zh-Hans"},{"language":"zh-Hant","href":"zh-Hant"}]}},"deployment":{"deploymentMetadataValue":{"subdomain":"extensionjs","hasAudioNarration":false,"actualSubdomain":"extensionjs","customDomains":["extension.js.org"],"gitSource":{"type":"github","owner":"extension-js","repo":"extension.js.org","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"feedback":{"thumbs":true,"agentFeedback":true},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6aee5e560d94937da094e6e1","clientVersion":"0.0.4102","searchFilterCounts":[{"language":"en","tag":"Documentation","count":143},{"language":"zh-Hans","tag":"Documentation","count":143},{"language":"zh-Hant","tag":"Documentation","count":143},{"language":"en","tag":"Implementation guide","count":26},{"language":"zh-Hans","tag":"Implementation guide","count":26},{"language":"zh-Hant","tag":"Implementation guide","count":26},{"language":"en","tag":"Workflows","count":17},{"language":"zh-Hans","tag":"Workflows","count":17},{"language":"zh-Hant","tag":"Workflows","count":17},{"language":"en","tag":"Commands reference","count":16},{"language":"zh-Hans","tag":"Commands reference","count":16},{"language":"zh-Hant","tag":"Commands reference","count":16},{"language":"en","tag":"Languages and frameworks","count":15},{"language":"zh-Hans","tag":"Languages and frameworks","count":15},{"language":"zh-Hant","tag":"Languages and frameworks","count":15},{"language":"en","tag":"Features","count":13},{"language":"zh-Hans","tag":"Features","count":13},{"language":"zh-Hant","tag":"Features","count":13},{"language":"en","tag":"Integrations","count":12},{"language":"zh-Hans","tag":"Integrations","count":12},{"language":"zh-Hant","tag":"Integrations","count":12},{"language":"en","tag":"Compare and migrate","count":9},{"language":"zh-Hans","tag":"Compare and migrate","count":9},{"language":"zh-Hant","tag":"Compare and migrate","count":9},{"language":"en","tag":"Browsers","count":8},{"language":"zh-Hans","tag":"Browsers","count":8},{"language":"zh-Hant","tag":"Browsers","count":8},{"language":"en","tag":"Blog","count":7},{"language":"zh-Hans","tag":"Blog","count":7},{"language":"zh-Hant","tag":"Blog","count":7}]},"assistantConfig":{"enableStarterQuestions":true,"enableContextualStarterQuestions":true,"deflection":{"enabled":true,"email":"hello@extension.dev"}},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[],"starterQuestions":[{"_id":"69e8bb9382f38263e5089464","deploymentId":"6648a29700dd75f3275ec70a","questionText":"Does Extension.js support Svelte?","status":"active","generatedBy":"manual","createdAt":"2026-04-22T12:14:11.430Z","updatedAt":"2026-04-22T12:14:11.430Z","__v":0},{"_id":"69e8bb7d82f38263e50893d7","deploymentId":"6648a29700dd75f3275ec70a","questionText":"How can I migrate my existing extension to Extension.js?","status":"active","generatedBy":"manual","createdAt":"2026-04-22T12:13:49.264Z","updatedAt":"2026-04-22T12:13:49.264Z","__v":0},{"_id":"69e8bb3d82f38263e50892a6","deploymentId":"6648a29700dd75f3275ec70a","questionText":"How can I create a browser extension with Extension.js?","status":"active","generatedBy":"manual","createdAt":"2026-04-22T12:12:45.272Z","updatedAt":"2026-04-22T12:12:45.272Z","__v":0}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":".ext-hero-cmd{display:inline-flex;align-items:center;gap:0.75rem;width:100%;max-width:36rem;padding:0.875rem 1rem 0.875rem 1.25rem;border-radius:0.75rem;background:rgba(15, 23, 42, 0.65);border:1px solid rgba(255, 255, 255, 0.12);font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;font-size:1rem;text-align:left;box-shadow:0 1px 0 rgba(255, 255, 255, 0.04) inset,\n 0 6px 24px rgba(0, 0, 0, 0.18)}:root:not(.dark) .ext-hero-cmd{background:rgba(15, 23, 42, 0.04);border-color:rgba(15, 23, 42, 0.12);box-shadow:0 1px 0 rgba(255, 255, 255, 0.6) inset,\n 0 6px 24px rgba(15, 23, 42, 0.06);color:#0f172a}.ext-hero-cmd-prompt{color:#0e62f7;font-weight:600;user-select:none}.ext-hero-cmd-code{flex:1;background:transparent!important;padding:0!important;color:inherit;white-space:nowrap;overflow-x:auto;font-size:1rem}.ext-hero-cmd-copy{flex:0 0 auto;padding:0.375rem 0.75rem;border-radius:0.5rem;border:1px solid rgba(255, 255, 255, 0.15);background:rgba(255, 255, 255, 0.04);color:inherit;font-size:0.8125rem;font-family:inherit;cursor:pointer;transition:background 0.15s ease,\n border-color 0.15s ease}.ext-hero-cmd-copy:hover{background:rgba(255, 255, 255, 0.08);border-color:rgba(255, 255, 255, 0.25)}:root:not(.dark) .ext-hero-cmd-copy{border-color:rgba(15, 23, 42, 0.22);background:rgba(15, 23, 42, 0.03);color:#334155}:root:not(.dark) .ext-hero-cmd-copy:hover{background:rgba(15, 23, 42, 0.07);border-color:rgba(15, 23, 42, 0.32)}.ext-hero-badges{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:0.5rem;margin-top:-0.25rem}.ext-hero-badges a{display:inline-flex;text-decoration:none}.ext-hero-badges img{height:22px;display:block}.ext-sponsors{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3rem;width:100%;max-width:32rem;margin:2rem auto 0}.ext-sponsor-tile{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:100%;height:72px;opacity:0.85;transition:opacity 0.15s ease}.ext-sponsor-tile:hover{opacity:1}.ext-sponsor-logo{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}.ext-sponsor-logo-circle{border-radius:50%;height:100%;width:auto}.ext-sponsor-logo-maro{max-height:40px;color:#1a1a1a}.ext-migrate-callout{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;gap:1.5rem;padding:2rem 2.5rem;border-radius:1.25rem;border:1px solid rgba(14, 98, 247, 0.25);background:linear-gradient(\n 135deg,\n rgba(14, 98, 247, 0.08) 0%,\n rgba(14, 98, 247, 0.02) 100%\n )}@media (min-width: 768px){.ext-migrate-callout{flex-direction:row;align-items:center}}.ext-migrate-actions{display:flex;flex-wrap:wrap;gap:0.75rem;flex-shrink:0}.ext-migrate-btn{display:inline-flex;align-items:center;padding:0.625rem 1rem;border-radius:0.625rem;border:1px solid rgba(14, 98, 247, 0.4);background:rgba(14, 98, 247, 0.05);color:#0e62f7!important;font-weight:500;font-size:0.9375rem;text-decoration:none!important;transition:background 0.15s ease,\n border-color 0.15s ease;white-space:nowrap}.ext-migrate-btn:hover{background:rgba(14, 98, 247, 0.12);border-color:rgba(14, 98, 247, 0.6)}.ext-hero-dock-wrap{display:flex;flex-direction:column;align-items:center;gap:0.625rem;margin-top:0.5rem}.ext-hero-dock-label{font-size:0.75rem;letter-spacing:0.08em;text-transform:uppercase;color:rgb(100, 116, 139);margin:0}.ext-dock-compact .ext-dock-row{gap:0.875rem}.ext-dock-compact .ext-dock-tile{width:36px;height:36px;padding:6px}.ext-codepreview{display:grid;grid-template-columns:1fr;gap:2rem;align-items:center;padding:2.25rem;border-radius:1.25rem;background:linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.02),\n rgba(255, 255, 255, 0.005)\n );border:1px solid rgba(255, 255, 255, 0.08)}@media (min-width: 1024px){.ext-codepreview{grid-template-columns:1fr 1.4fr;gap:3rem;padding:3rem}}.ext-codepreview-text code{background:rgba(14, 98, 247, 0.08);color:#0e62f7;padding:0.05em 0.4em;border-radius:0.3rem;font-size:0.95em}.ext-codepreview-code{background:rgba(15, 23, 42, 0.7)!important;color:#e2e8f0!important;padding:1.25rem 1.5rem!important;border-radius:0.75rem;border:1px solid rgba(255, 255, 255, 0.08);overflow-x:auto;font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;font-size:0.875rem;line-height:1.7;margin:0}.ext-cp-punc{color:#94a3b8}.ext-cp-key{color:#93c5fd}.ext-cp-str{color:#86efac}.ext-cp-num{color:#fbbf24}.ext-cp-prefix{color:#f0abfc;font-weight:600}:root:not(.dark) .ext-codepreview{background:linear-gradient(\n 180deg,\n rgba(15, 23, 42, 0.02),\n rgba(15, 23, 42, 0.005)\n );border-color:rgba(15, 23, 42, 0.08)}:root:not(.dark) .ext-codepreview-code{background:#f1f5f9!important;color:#0f172a!important;border-color:rgba(15, 23, 42, 0.1)}:root:not(.dark) .ext-cp-punc{color:#475569}:root:not(.dark) .ext-cp-key{color:#1d4ed8}:root:not(.dark) .ext-cp-str{color:#15803d}:root:not(.dark) .ext-cp-num{color:#9a3412}:root:not(.dark) .ext-cp-prefix{color:#a21caf}.ext-feature-grid{display:grid;grid-template-columns:1fr;gap:1rem}@media (min-width: 1024px){.ext-feature-grid{grid-template-columns:repeat(3, minmax(0, 1fr));grid-template-areas:\"a a b\"\n \"c d d\"\n \"e e f\";gap:1rem}.ext-fa{grid-area:a}.ext-fb{grid-area:b}.ext-fc{grid-area:c}.ext-fd{grid-area:d}.ext-fe{grid-area:e}.ext-ff{grid-area:f}}.ext-feature-card{display:flex;flex-direction:column;justify-content:space-between;gap:1rem;padding:1.75rem;border-radius:1rem;border:1px solid rgba(255, 255, 255, 0.08);background:linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.02) 0%,\n rgba(255, 255, 255, 0.01) 100%\n );text-decoration:none!important;color:inherit!important;transition:border-color 0.15s ease,\n background 0.15s ease;min-height:190px}.ext-template-grid{display:grid;grid-template-columns:1fr;gap:1rem}@media (min-width: 768px){.ext-template-grid{grid-template-columns:repeat(2, minmax(0, 1fr))}}.ext-feature-card:hover{border-color:rgba(14, 98, 247, 0.45);background:rgba(14, 98, 247, 0.05)}.ext-feature-cta{font-weight:600;color:#0e62f7;white-space:nowrap}:root:not(.dark) .ext-feature-card{border-color:rgba(0, 0, 0, 0.08);background:#fff}:root:not(.dark) .ext-feature-card:hover{border-color:rgba(14, 98, 247, 0.45);background:#f8fafc}:root .ext-feature-card{border-bottom-color:rgba(255, 255, 255, 0.08)}:root .ext-feature-card:hover{border-bottom-color:rgba(14, 98, 247, 0.45)}:root:not(.dark) .ext-feature-card{border-bottom-color:rgba(0, 0, 0, 0.08)}:root:not(.dark) .ext-feature-card:hover{border-bottom-color:rgba(14, 98, 247, 0.45)}.ext-dock{max-width:43.125rem;display:flex;flex-direction:column;gap:0.75rem}.ext-dock-row{display:grid;gap:0.75rem;width:100%}.ext-dock-row-top{grid-template-columns:repeat(5, minmax(0, 1fr))}.ext-dock-row-bottom{grid-template-columns:repeat(4, minmax(0, 1fr));padding:0 10%}.frame>.relative,.frame .relative>iframe{border-radius:8px}@supports (corner-shape: superellipse(1.426)){.ext-feature-card,.ext-codepreview,.ext-migrate-callout{corner-shape:superellipse(1.426)}.ext-feature-card{border-radius:1.272rem}.ext-codepreview,.ext-migrate-callout{border-radius:1.59rem}}.ext-dock-tile{aspect-ratio:1 / 1;border-radius:27.2%;overflow:hidden;background:#fff;border:1px solid rgba(255, 255, 255, 0.08);box-shadow:inset 0 1px 1px 0 rgba(255, 255, 255, 0.06);display:flex;align-items:center;justify-content:center}:root:not(.dark) .ext-dock-tile{border-color:rgba(0, 0, 0, 0.08);box-shadow:0 1px 2px 0 rgba(0, 0, 0, 0.04)}@supports (corner-shape: superellipse(1.5)){.ext-dock-tile{border-radius:36%;corner-shape:superellipse(1.5)}}.ext-dock-tile img{width:100%;height:100%;object-fit:contain;padding:7%}.ext-dock-tile.plain{background:transparent;border:none;box-shadow:none}.ext-dock-tile.plain img{padding:0;object-fit:cover}.ext-footer{width:100%;border-top:1px solid rgba(255, 255, 255, 0.08);margin-top:1rem;padding-top:2rem}:root:not(.dark) .ext-footer{border-top-color:rgba(0, 0, 0, 0.08)}.ext-footer-inner{display:flex;flex-direction:column;align-items:flex-start;justify-content:space-between;gap:2rem}@media (min-width: 768px){.ext-footer-inner{flex-direction:row;align-items:center}}.ext-footer-cmd code{display:inline-block;padding:0.5rem 0.875rem;border-radius:0.5rem;border:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.04);font-size:0.875rem;font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;color:inherit}:root:not(.dark) .ext-footer-cmd code{border-color:rgba(0, 0, 0, 0.08);background:#f8fafc}.ext-footer-right{display:flex;flex-direction:column;gap:1rem}@media (min-width: 768px){.ext-footer-right{align-items:flex-end}}.ext-footer-links{list-style:none;padding:0;margin:0;display:flex;flex-wrap:wrap;align-items:center;gap:0.5rem 2rem}.ext-footer-links a{display:inline-flex;align-items:center;gap:0.25rem;font-size:0.875rem;color:rgba(255, 255, 255, 0.6);text-decoration:none;transition:color 0.15s ease}.ext-footer-links a:hover{color:#fff;text-decoration:underline;text-underline-offset:4px}:root:not(.dark) .ext-footer-links a{color:rgba(0, 0, 0, 0.6)}:root:not(.dark) .ext-footer-links a:hover{color:#000}.ext-footer-copy{font-size:0.875rem;color:rgba(255, 255, 255, 0.6);margin:0}:root:not(.dark) .ext-footer-copy{color:rgba(0, 0, 0, 0.6)}.ext-footer-copy a{color:inherit;text-decoration:underline;text-underline-offset:4px;transition:color 0.15s ease}.ext-footer-copy a:hover{color:#fff}:root:not(.dark) .ext-footer-copy a:hover{color:#000}.ext-usedby-grid{display:grid;grid-template-columns:1fr;gap:1rem;width:100%}@media (min-width: 768px){.ext-usedby-grid{grid-template-columns:repeat(2, minmax(0, 1fr))}}.ext-usedby-card{position:relative;overflow:hidden;min-height:20rem;border-radius:1rem;border:1px solid rgba(255, 255, 255, 0.08);background:rgba(255, 255, 255, 0.02);transition:border-color 0.15s ease}.ext-usedby-card:hover{border-color:rgba(255, 255, 255, 0.16)}.ext-usedby-body{position:relative;z-index:1;display:flex;flex-direction:column;gap:1rem;height:100%;padding:1.5rem}.ext-usedby-head{display:flex;align-items:flex-start;gap:1rem}.ext-usedby-titles{flex:1;min-width:0}.ext-usedby-name{margin:0!important;font-size:1.25rem;font-weight:500;line-height:1.3}.ext-usedby-meta{margin:0.25rem 0 0!important;font-size:0.75rem;line-height:1.4;color:#a1a1aa}.ext-usedby-pill{display:inline-flex;flex:none;align-items:center;gap:0.3rem;padding:0.2rem 0.55rem;border-radius:999px;border:1px solid rgba(255, 255, 255, 0.12);font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n monospace;font-size:0.75rem;color:#a1a1aa}.ext-usedby-desc{display:-webkit-box;min-height:3rem;margin:0!important;overflow:hidden;font-size:0.875rem;line-height:1.5rem;color:#a1a1aa;-webkit-line-clamp:2;-webkit-box-orient:vertical}.ext-usedby-shot{position:absolute;right:0.125rem;bottom:-2.5rem;z-index:0;width:clamp(160px, 40%, 240px);aspect-ratio:16 / 10;overflow:hidden;border-radius:0.5rem;background:#18181b;box-shadow:0 18px 36px -10px rgba(0, 0, 0, 0.55);pointer-events:none;transform:rotate(-6deg);transform-origin:top left;transition:transform 150ms cubic-bezier(0, 0, 0.2, 1)}.ext-usedby-card:hover .ext-usedby-shot{transform:rotate(-3deg) translate(-3px, -6px)}.ext-usedby-shot img{display:block;width:100%;height:100%;margin:0!important;object-fit:cover;object-position:top left}@media (prefers-reduced-motion: reduce){.ext-usedby-shot{transition:none}}:root:not(.dark) .ext-usedby-card{border-color:rgba(15, 23, 42, 0.1);background:#ffffff}:root:not(.dark) .ext-usedby-card:hover{border-color:rgba(15, 23, 42, 0.2)}:root:not(.dark) .ext-usedby-meta,:root:not(.dark) .ext-usedby-pill,:root:not(.dark) .ext-usedby-desc{color:#52525b}:root:not(.dark) .ext-usedby-pill{border-color:rgba(15, 23, 42, 0.12)}:root:not(.dark) .ext-usedby-shot{background:#f4f4f5;box-shadow:0 18px 36px -10px rgba(15, 23, 42, 0.3)}.ext-usedby-link{color:#60a5fa!important;text-decoration:underline!important;text-underline-offset:3px}:root:not(.dark) .ext-usedby-link{color:#0e62f7!important}.ext-usedby-logo{width:96px}.ext-usedby-eyebrow{margin:0!important;font-size:0.875rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:#60a5fa}:root:not(.dark) .ext-usedby-eyebrow{color:#0e62f7}.ext-usedby-steps{display:flex;flex-direction:column;gap:0.75rem;margin:1.5rem 0 0!important;padding:0!important;list-style:none}.ext-usedby-steps li{display:flex;align-items:flex-start;gap:0.75rem;margin:0!important;padding:0!important;font-size:1rem;line-height:1.75rem}.ext-usedby-step-num{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border-radius:999px;border:1px solid rgba(14, 98, 247, 0.45);background:rgba(14, 98, 247, 0.16);color:#93c5fd;font-size:0.8125rem;font-weight:600;line-height:1}:root:not(.dark) .ext-usedby-step-num{border-color:rgba(14, 98, 247, 0.35);background:rgba(14, 98, 247, 0.08);color:#0e62f7}.ext-usedby-note{margin:1.25rem 0 0!important;max-width:28rem;font-size:0.875rem;line-height:1.5rem;color:#a1a1aa}:root:not(.dark) .ext-usedby-note{color:#52525b}.ext-usedby-add-code{min-width:0}.ext-usedby-add-code>*{margin-top:0!important;margin-bottom:0!important}.ext-usedby-icon{flex:none;width:56px;height:56px;margin:0!important;object-fit:contain}.ext-usedby-icon-avatar{border-radius:22%;object-fit:cover}:root.dark .ext-usedby-icon-invert{filter:invert(1)}.ext-usedby-links{margin:auto 0 0!important;font-size:0.8125rem;line-height:1.5rem;color:#a1a1aa}.ext-usedby-card-shot .ext-usedby-links{max-width:62%}.ext-usedby-links a{border-bottom:none!important;color:#60a5fa!important;font-weight:500;text-decoration:none!important}.ext-usedby-links a:hover{text-decoration:underline!important;text-underline-offset:3px}:root:not(.dark) .ext-usedby-links{color:#52525b}:root:not(.dark) .ext-usedby-links a{color:#0e62f7!important}.ext-usedby-slot{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.5rem;min-height:20rem;padding:1.5rem;border-radius:1rem;border:1px dashed rgba(255, 255, 255, 0.18)!important;color:inherit!important;text-align:center;text-decoration:none!important;transition:border-color 0.15s ease}.ext-usedby-slot:hover{border-color:rgba(14, 98, 247, 0.6)!important}.ext-usedby-slot-title{font-size:1.25rem;font-weight:500}.ext-usedby-slot-text{max-width:18rem;font-size:0.875rem;line-height:1.5rem;color:#a1a1aa}:root:not(.dark) .ext-usedby-slot{border-color:rgba(15, 23, 42, 0.2)!important}:root:not(.dark) .ext-usedby-slot-text{color:#52525b}.nav-tabs a.nav-tabs-item[target=\"_blank\"]::after{content:\"\";display:inline-block;flex:none;width:10px;height:10px;background-color:#0e62f7;-webkit-mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\");mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:contain;mask-size:contain}:root.dark .nav-tabs a.nav-tabs-item[target=\"_blank\"]::after{background-color:#60a5fa}.ext-usedby-meta a,.ext-usedby-links a{border-bottom:none!important;color:#60a5fa!important;font-weight:500;text-decoration:none!important}.ext-usedby-meta a:hover{text-decoration:underline!important;text-underline-offset:3px}:root:not(.dark) .ext-usedby-meta a{color:#0e62f7!important}.ext-usedby-links-label{display:block;margin:0 0 0.15rem;font-size:0.6875rem;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:#71717a}:root:not(.dark) .ext-usedby-links-label{color:#71717a}.ext-usedby-sepline{display:block;overflow:hidden}.ext-usedby-sepline-row{display:flex;flex-wrap:wrap;margin-left:-1rem}.ext-usedby-sepline-item{white-space:nowrap}.ext-usedby-sepline-item::before{content:\"•\";display:inline-block;width:1rem;color:#52525b;text-align:center}:root:not(.dark) .ext-usedby-sepline-item::before{color:#a1a1aa}.ext-usedby-grid{grid-auto-rows:1fr}.ext-usedby-store-list{display:flex;flex-direction:column;gap:0.1rem;margin:0!important;padding:0!important;list-style:none}.ext-usedby-store-list li{margin:0!important;padding:0!important}.ext-usedby-store{display:inline-flex;align-items:center;gap:0.35rem;white-space:nowrap}.ext-usedby-store::after{content:\"\";flex:none;width:10px;height:10px;background-color:#60a5fa;-webkit-mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\");mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:contain;mask-size:contain}:root:not(.dark) .ext-usedby-store::after{background-color:#0e62f7}.ext-usedby-meta a.ext-usedby-author{display:inline-flex;align-items:center;gap:0.35rem;color:#a1a1aa!important;font-weight:400}.ext-usedby-meta a.ext-usedby-author:hover{color:#e4e4e7!important;text-decoration:none!important}.ext-usedby-author-avatar{flex:none;width:16px;height:16px;margin:0!important;border-radius:50%}:root:not(.dark) .ext-usedby-meta a.ext-usedby-author{color:#52525b!important}:root:not(.dark) .ext-usedby-meta a.ext-usedby-author:hover{color:#18181b!important}.ext-usedby-ext{display:inline-flex;align-items:center;gap:0.3rem}.ext-usedby-source::before,.ext-usedby-ext::after{content:\"\";flex:none;background-color:currentColor;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:contain;mask-size:contain}.ext-usedby-source::before{width:12px;height:12px;-webkit-mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/brands/github.svg\");mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/brands/github.svg\")}.ext-usedby-ext::after{width:10px;height:10px;-webkit-mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\");mask-image:url(\"https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/arrow-up-right-from-square.svg\")}"}],"page":{"pagePath":"docs/implementation-guide/background","slug":"/docs/implementation-guide/background","pageMetadata":{"title":"Background scripts and MV3 service workers","description":"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.","keywords":["extension.js","extensions","firefox","browser","manifest","javascript","hmr","content script"],"href":"/docs/implementation-guide/background"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,CodeGroup,Heading,OptimizedImage,Table,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!CodeGroup)_missingMdxReference(`CodeGroup`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!OptimizedImage)_missingMdxReference(`OptimizedImage`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(_components.p,{children:`Run extension-wide logic in background contexts with clear support for both Manifest V2 (MV2) background scripts and Manifest V3 (MV3) service workers.`}),`\n`,_jsxs(_components.p,{children:[`Extension.js reads background entries from `,_jsx(_components.code,{children:`manifest.json`}),` and compiles them into dedicated background outputs. It applies browser-specific reload behavior during development.`]}),`\n`,_jsx(Heading,{level:`2`,id:`template-examples`,children:`Template examples`}),`\n`,_jsx(Heading,{level:`3`,id:`action`,children:_jsx(_components.code,{children:`action`})}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=5e4f6a24e0d133524a58b78e0a1d7585`,alt:`action template screenshot`,\"data-og-width\":`2400`,width:`2400`,\"data-og-height\":`1800`,height:`1800`,\"data-path\":`images/examples/action/screenshot.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=280\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=c19749cb40d93bd08e00f790addc618f 280w, https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=560\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=d0c6bcb5a169185a2aff94e1ac82dd03 560w, https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=840\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=c10372d7f3f4a9e70a644eae13f2c5c0 840w, https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=1100\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=38c9ca9d920699efa1bf625d1e587fc9 1100w, https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=1650\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=a12941a62b82166a1e1c4a729ca73731 1650w, https://mintcdn.com/extensionjs/VCnDd7fX2Nza24SE/images/examples/action/screenshot.png?w=2500\u0026fit=max\u0026auto=format\u0026n=VCnDd7fX2Nza24SE\u0026q=85\u0026s=1fcf9dc451920102068315ce54ae349d 2500w`}),`\n`,_jsx(_components.p,{children:`An action popup extension with a background service worker handling browser events.`}),`\n`,_jsxs(CodeGroup,{children:[_jsx(CodeBlock,{filename:`npm`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`npx`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` extension@latest create my-extension --template=action`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`pnpm`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`pnpx extension@latest create my-extension --template=action`})}),`\n`]})})}),_jsx(CodeBlock,{filename:`yarn 2+`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`yarn`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` dlx extension@latest create my-extension --template=action`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`bun`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`bunx extension@latest create my-extension --template=action`})}),`\n`]})})}),_jsx(CodeBlock,{filename:`deno`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`deno run -A `}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`npm`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:extension@latest create my-extension --template=action`})]}),`\n`]})})})]}),`\n`,_jsxs(_components.p,{children:[`Repository: `,_jsx(_components.a,{href:`https://github.com/extension-js/examples/tree/main/examples/action`,children:`extension-js/examples/action`})]}),`\n`,_jsx(Heading,{level:`3`,id:`ai-chatgpt`,children:_jsx(_components.code,{children:`ai-chatgpt`})}),`\n`,_jsx(OptimizedImage,{src:`https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=1a90c26593cbc1539918278ed5aa4340`,alt:`ai-chatgpt template screenshot`,\"data-og-width\":`2400`,width:`2400`,\"data-og-height\":`1800`,height:`1800`,\"data-path\":`images/examples/ai-chatgpt/screenshot.png`,\"data-optimize\":`true`,\"data-opv\":`3`,srcset:`https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=280\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=2fcef235e7a3a2e91908bc25d146d834 280w, https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=560\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=cef89e867b35dadf223b92a7b878cbf5 560w, https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=840\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=dd46646e3e3f15e7e3b307b422631e9d 840w, https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=1100\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=05ab7a709198a26988b9e602c3552a08 1100w, https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=1650\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=1f9c0ac4887ca87039a5b59170d93229 1650w, https://mintcdn.com/extensionjs/06It--Hh5dUVeN6m/images/examples/ai-chatgpt/screenshot.png?w=2500\u0026fit=max\u0026auto=format\u0026n=06It--Hh5dUVeN6m\u0026q=85\u0026s=b879928469b4df5708330927fe23fd62 2500w`}),`\n`,_jsx(_components.p,{children:`Action popup with a background service worker that integrates with an external API (ChatGPT).`}),`\n`,_jsxs(CodeGroup,{children:[_jsx(CodeBlock,{filename:`npm`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`npx`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` extension@latest create my-extension --template=ai-chatgpt`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`pnpm`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`pnpx extension@latest create my-extension --template=ai-chatgpt`})}),`\n`]})})}),_jsx(CodeBlock,{filename:`yarn 2+`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`yarn`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` dlx extension@latest create my-extension --template=ai-chatgpt`})]}),`\n`]})})}),_jsx(CodeBlock,{filename:`bun`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`bunx extension@latest create my-extension --template=ai-chatgpt`})}),`\n`]})})}),_jsx(CodeBlock,{filename:`deno`,numberOfLines:`1`,language:`shellscript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`shellscript`,children:_jsxs(_components.code,{language:`shellscript`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`deno run -A `}),_jsx(_components.span,{style:{color:`#8250DF`,\"--shiki-dark\":`#DCDCAA`},children:`npm`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:extension@latest create my-extension --template=ai-chatgpt`})]}),`\n`]})})})]}),`\n`,_jsxs(_components.p,{children:[`Repository: `,_jsx(_components.a,{href:`https://github.com/extension-js/examples/tree/main/examples/ai-chatgpt`,children:`extension-js/examples/ai-chatgpt`})]}),`\n`,_jsx(Heading,{level:`2`,id:`background-capabilities`,children:`Background capabilities`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Capability`}),_jsx(_components.th,{children:`What it gives you`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`MV2/MV3 compatibility`}),_jsxs(_components.td,{children:[`Support `,_jsx(_components.code,{children:`background.scripts`}),` and `,_jsx(_components.code,{children:`background.service_worker`}),` entries`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Dedicated background outputs`}),_jsx(_components.td,{children:`Emit background runtime files per active manifest shape`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Reload-aware development`}),_jsx(_components.td,{children:`Apply hard reload/restart behavior for structural changes`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:`Worker mode control`}),_jsxs(_components.td,{children:[`Respect `,_jsx(_components.code,{children:`background.type`}),` module/classic runtime behavior`]})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`background-script-support`,children:`Background script support`}),`\n`,_jsxs(_components.p,{children:[`Declare background scripts with these `,_jsx(_components.code,{children:`manifest.json`}),` fields:`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Manifest field`}),_jsx(_components.th,{children:`File type expected`}),_jsx(_components.th,{children:`dev behavior`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`background.service_worker`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`.js`}),`, `,_jsx(_components.code,{children:`.ts`}),`, `,_jsx(_components.code,{children:`.mjs`}),`, `,_jsx(_components.code,{children:`.tsx`})]}),_jsx(_components.td,{children:`hard reload flow`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`background.scripts`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`.js`}),`, `,_jsx(_components.code,{children:`.ts`}),`, `,_jsx(_components.code,{children:`.mjs`}),`, `,_jsx(_components.code,{children:`.tsx`})]}),_jsx(_components.td,{children:`hot module replacement (HMR)-compatible path`})]})]})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`background.type`}),` can also affect runtime mode (`,_jsx(_components.code,{children:`module`}),` vs classic service worker behavior).`]}),`\n`,_jsx(Heading,{level:`2`,id:`sample-background-script-declaration`,children:`Sample background script declaration`}),`\n`,_jsxs(_components.p,{children:[`Example background script declaration in `,_jsx(_components.code,{children:`manifest.json`}),`:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`8`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`8`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"manifest_version\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`3`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"name\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"My Extension\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"version\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"1.0.0\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"background\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"service_worker\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"./scripts/background.ts\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`development-behavior`,children:`Development behavior`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Extension.js tracks service worker/source changes and can trigger hard extension reload.`}),`\n`,_jsx(_components.li,{children:`Manifest changes affecting background entries can trigger restart-required diagnostics.`}),`\n`,_jsx(_components.li,{children:`Browser launch plugins apply target-specific hard reload strategies (Chromium/Firefox).`}),`\n`,_jsx(_components.li,{children:`Extension.js treats structural entrypoint changes more strictly than regular module edits.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`firefox-service-worker-translated-to-an-event-page`,children:`Firefox: service worker translated to an event page`}),`\n`,_jsxs(_components.p,{children:[`Firefox does not run MV3 `,_jsx(_components.code,{children:`background.service_worker`}),` (loading such an add-on fails with `,_jsx(_components.em,{children:`“background.service_worker is currently disabled. Add background.scripts.”`}),`). You don’t need to special-case it: for the Firefox build, Extension.js automatically translates a `,_jsx(_components.code,{children:`background.service_worker`}),` entry into a `,_jsx(_components.code,{children:`background.scripts`}),` event page pointing at the same compiled bundle. Author one `,_jsx(_components.code,{children:`background.service_worker`}),` and it loads on both Chromium and Firefox.`]}),`\n`,_jsx(Heading,{level:`2`,id:`chromium-scripts-translated-to-a-service-worker`,children:`Chromium: scripts translated to a service worker`}),`\n`,_jsxs(_components.p,{children:[`The translation runs in the other direction too. In a Chromium MV3 build, a `,_jsx(_components.code,{children:`background.scripts`}),` array becomes `,_jsx(_components.code,{children:`background.service_worker`}),` pointing at the compiled bundle.`]}),`\n`,_jsxs(_components.p,{children:[`Chromium MV3 rejects the whole extension whenever `,_jsx(_components.code,{children:`background.scripts`}),` is present, even next to a valid `,_jsx(_components.code,{children:`service_worker`}),`. Extension.js therefore always removes the `,_jsx(_components.code,{children:`scripts`}),` key from the emitted Chromium MV3 manifest. When the source declares both fields, the existing `,_jsx(_components.code,{children:`service_worker`}),` entry wins.`]}),`\n`,_jsx(Heading,{level:`2`,id:`safari-a-background-page-not-a-service-worker`,children:`Safari: a background page, not a service worker`}),`\n`,_jsxs(_components.p,{children:[`From 4.1.20, Safari builds emit a non-persistent background page. Extension.js translates a `,_jsx(_components.code,{children:`background.service_worker`}),` entry into `,_jsx(_components.code,{children:`background: {scripts: [...]}`}),`, the same translation it runs for Firefox.`]}),`\n`,_jsxs(_components.p,{children:[`The reason was measured. A Manifest V3 service worker never started on Safari 26.5.2, including for extensions built by Apple’s own converter. A background page ran immediately. WebKit deliberately prefers the page form (`,_jsx(_components.a,{href:`https://bugs.webkit.org/show_bug.cgi?id=270750`,children:`WebKit bug 270750`}),`).`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`One unguarded Chromium-only call at the `,_jsx(_components.strong,{children:`top level`}),` of a background script\nthrows on Safari, and Safari then discards the background context. You get no\nlogs, no reload, and no error, so the extension looks dead. Guard the\ncall, or branch at build time.`]})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`javascript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`javascript`,children:_jsxs(_components.code,{language:`javascript`,numberOfLines:`5`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// Throws on Safari and takes the whole background context with it.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`true`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` });`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`// Safe on every target.`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`chrome.sidePanel?.setPanelBehavior({ openPanelOnActionClick: `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`true`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` });`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Safari builds from 4.1.20 also warn about the extension APIs that Safari lacks, which is the cheapest way to find this before you start debugging. See `,_jsx(_components.a,{href:`/docs/browsers/safari`,children:`Building Safari extensions`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`mv3-service-worker-lifecycle`,children:`MV3 service worker lifecycle`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`background.service_worker`}),` runs under the browser’s service-worker lifecycle, not as a long-running process.`]}),`\n`,_jsx(_components.p,{children:`That means:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`In-memory state can disappear between events.`}),`\n`,_jsx(_components.li,{children:`Design long-running work around events, not process permanence.`}),`\n`,_jsx(_components.li,{children:`Startup should stay small and predictable.`}),`\n`,_jsx(_components.li,{children:`Your code should restore important state from storage or recompute it safely.`}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Use `,_jsx(_components.code,{children:`background.scripts`}),` only for MV2-style compatibility cases. For MV3-first extensions, treat the service worker as the central coordinator for browser API calls and cross-context communication.`]}),`\n`,_jsx(Heading,{level:`2`,id:`output-behavior`,children:`Output behavior`}),`\n`,_jsx(_components.p,{children:`Common outputs include:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:_jsx(_components.code,{children:`background/service_worker.js`})}),`\n`,_jsx(_components.li,{children:_jsx(_components.code,{children:`background/scripts.js`})}),`\n`]}),`\n`,_jsx(_components.p,{children:`The exact output depends on which manifest field remains active after browser filtering.`}),`\n`,_jsx(Heading,{level:`2`,id:`module-and-classic-notes`,children:`Module and classic notes`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`background.type: \"module\"`}),` uses module worker semantics.`]}),`\n`,_jsxs(_components.li,{children:[`Classic service worker mode uses `,_jsx(_components.code,{children:`importScripts`}),`-based chunk loading behavior.`]}),`\n`,_jsx(_components.li,{children:`Avoid dynamic imports in background code that must load immediately on startup.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`runtime-loaded-files`,children:`Runtime-loaded files`}),`\n`,_jsx(_components.p,{children:`Some files never appear as import statements but still must ship with the extension. Extension.js traces these into the output so the built extension behaves like the source loaded unpacked in the browser:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`importScripts(...)`}),` dependencies of classic service workers.`]}),` String-literal arguments are resolved recursively (a dependency can call `,_jsx(_components.code,{children:`importScripts`}),` itself) against the emitted worker location, so worker-relative URLs keep working even though the worker moves to `,_jsx(_components.code,{children:`background/service_worker.js`}),`. WebAssembly sibling files loaded by wasm-bindgen glue (`,_jsx(_components.code,{children:`*_bg.wasm`}),`) ship alongside their loader.`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`chrome.scripting.executeScript({files: [...]})`}),` and `,_jsx(_components.code,{children:`insertCSS({files: [...]})`}),` payloads.`]}),` Files referenced only from `,_jsx(_components.code,{children:`files:`}),` arrays (not declared in manifest `,_jsx(_components.code,{children:`content_scripts`}),`) are copied verbatim at their extension-root paths.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`A referenced file that does not exist in your project produces a build warning naming the missing path, instead of a silent runtime failure.`}),`\n`,_jsx(Heading,{level:`2`,id:`recommended-architecture`,children:`Recommended architecture`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Keep the background entry thin and move feature logic into shared modules.`}),`\n`,_jsx(_components.li,{children:`Use the background context as the central coordinator for messaging, storage access, and browser API calls.`}),`\n`,_jsx(_components.li,{children:`Restore durable state from storage instead of assuming the background script stays running permanently.`}),`\n`,_jsx(_components.li,{children:`Use alarms, explicit event listeners, and small feature modules instead of one large startup path.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`common-mistakes`,children:`Common mistakes`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Treating the service worker like a permanently running server process.`}),`\n`,_jsx(_components.li,{children:`Keeping critical state only in memory.`}),`\n`,_jsx(_components.li,{children:`Doing expensive startup work on every event wakeup.`}),`\n`,_jsx(_components.li,{children:`Putting too much feature logic in popup or content-script code when it actually needs background-level browser API access.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`best-practices`,children:`Best practices`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Prefer `,_jsx(_components.code,{children:`background.service_worker`}),` for MV3-first extensions.`]}),`\n`,_jsx(_components.li,{children:`Keep background entry files small and delegate logic to shared modules.`}),`\n`,_jsx(_components.li,{children:`Avoid expensive startup work in service workers; initialize lazily where safe.`}),`\n`,_jsx(_components.li,{children:`Treat manifest background field edits as structural changes in dev flow.`}),`\n`,_jsx(_components.li,{children:`Route privileged actions through validated message handlers.`}),`\n`,_jsx(_components.li,{children:`Store durable settings and caches in browser storage, not only in module-level variables.`}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`next-steps`,children:`Next steps`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Understand update outcomes in `,_jsx(_components.a,{href:`/docs/workflows/dev-update-behavior`,children:`dev update behavior`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Persist durable runtime state with `,_jsx(_components.a,{href:`/docs/implementation-guide/storage`,children:`Storage`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Coordinate contexts with `,_jsx(_components.a,{href:`/docs/implementation-guide/messaging`,children:`Messaging`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Learn more about `,_jsx(_components.a,{href:`/docs/implementation-guide/javascript`,children:`JavaScript in development`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Continue with `,_jsx(_components.a,{href:`/docs/implementation-guide/content-scripts`,children:`content scripts`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Troubleshoot service worker behavior in `,_jsx(_components.a,{href:`/docs/concepts/manifest-v3`,children:`Manifest V3 troubleshooting`}),`.`]}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Background scripts and MV3 service workers","description":"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.","keywords":["extension.js","extensions","firefox","browser","manifest","javascript","hmr","content script"],"href":"/docs/implementation-guide/background"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Template examples","slug":"template-examples","depth":2,"children":[{"title":"action","slug":"action","depth":3,"children":[]},{"title":"ai-chatgpt","slug":"ai-chatgpt","depth":3,"children":[]}]},{"title":"Background capabilities","slug":"background-capabilities","depth":2,"children":[]},{"title":"Background script support","slug":"background-script-support","depth":2,"children":[]},{"title":"Sample background script declaration","slug":"sample-background-script-declaration","depth":2,"children":[]},{"title":"Development behavior","slug":"development-behavior","depth":2,"children":[]},{"title":"Firefox: service worker translated to an event page","slug":"firefox-service-worker-translated-to-an-event-page","depth":2,"children":[]},{"title":"Chromium: scripts translated to a service worker","slug":"chromium-scripts-translated-to-a-service-worker","depth":2,"children":[]},{"title":"Safari: a background page, not a service worker","slug":"safari-a-background-page-not-a-service-worker","depth":2,"children":[]},{"title":"MV3 service worker lifecycle","slug":"mv3-service-worker-lifecycle","depth":2,"children":[]},{"title":"Output behavior","slug":"output-behavior","depth":2,"children":[]},{"title":"Module and classic notes","slug":"module-and-classic-notes","depth":2,"children":[]},{"title":"Runtime-loaded files","slug":"runtime-loaded-files","depth":2,"children":[]},{"title":"Recommended architecture","slug":"recommended-architecture","depth":2,"children":[]},{"title":"Common mistakes","slug":"common-mistakes","depth":2,"children":[]},{"title":"Best practices","slug":"best-practices","depth":2,"children":[]},{"title":"Next steps","slug":"next-steps","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"extensionjs","siteName":"Extension.js","title":"Background scripts and MV3 service workers - Extension.js","contextualStarterQuestions":[{"_id":"6aab617cbe0470569c0a7cfe","deploymentId":"6648a29700dd75f3275ec70a","questionText":"What's the difference MV2 vs MV3?","status":"active","generatedBy":"contextual","path":"docs/implementation-guide/background","createdAt":"2026-09-17T03:41:48.659Z","updatedAt":"2026-09-17T03:41:48.659Z","__v":0},{"_id":"6aab617cbe0470569c0a7cfd","deploymentId":"6648a29700dd75f3275ec70a","questionText":"How do I set up background scripts?","status":"active","generatedBy":"contextual","path":"docs/implementation-guide/background","createdAt":"2026-09-17T03:41:48.659Z","updatedAt":"2026-09-17T03:41:48.659Z","__v":0},{"_id":"6aab617cbe0470569c0a7cff","deploymentId":"6648a29700dd75f3275ec70a","questionText":"How do service workers reload?","status":"active","generatedBy":"contextual","path":"docs/implementation-guide/background","createdAt":"2026-09-17T03:41:48.659Z","updatedAt":"2026-09-17T03:41:48.659Z","__v":0}],"navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{"en":{"href":"/docs/implementation-guide/background","matchLevel":0},"zh-Hans":{"href":"/zh-Hans/docs/implementation-guide/background","matchLevel":1},"zh-Hant":{"href":"/zh-Hant/docs/implementation-guide/background","matchLevel":1}}},"lastModified":"2026-09-17T03:41:32.063Z","originalFileLocation":"docs/implementation-guide/background.mdx","locale":"en"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"dark\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Extension.js\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Background scripts and MV3 service workers - Extension.js\">\u003cmeta data-mint-head property=\"og:description\" content=\"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Extension.js\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://extension.js.org/docs/implementation-guide/background\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://media.extension.land/brand/extension-js/social/og.png\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Background scripts and MV3 service workers - Extension.js\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://media.extension.land/brand/extension-js/social/og.png\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/extensionjs/hqzmAu5S4StlssPO/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Extension.js\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0e62f7\">\u003cmeta data-mint-head name=\"og:image:type\" content=\"image/png\">\u003cmeta data-mint-head name=\"og:image:alt\" content=\"Extension.js, the cross-browser extension framework\">\u003cmeta data-mint-head name=\"twitter:creator\" content=\"@extensionjs\">\u003cmeta data-mint-head name=\"twitter:image:alt\" content=\"Extension.js, the cross-browser extension framework\">\u003cmeta data-mint-head name=\"google-site-verification\" content=\"BLSk7tcWG_qVtuAAf4UJyUbPIGXfghiJcUaDP4aoGpM\">\u003cmeta data-mint-head name=\"msvalidate.01\" content=\"E718A8AA0D4E3ED605BFF1BA1C0E46A7\">\u003cmeta data-mint-head name=\"yandex-verification\" content=\"e2a6f3f5424c1722\">\u003clink data-mint-head rel=\"canonical\" href=\"https://extension.js.org/docs/implementation-guide/background\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/implementation-guide/background.md\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"en\" href=\"https://extension.js.org/docs/implementation-guide/background\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"zh-Hans\" href=\"https://extension.js.org/zh-Hans/docs/implementation-guide/background\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"zh-Hant\" href=\"https://extension.js.org/zh-Hant/docs/implementation-guide/background\">\u003clink data-mint-head rel=\"alternate\" hreflang=\"x-default\" href=\"https://extension.js.org/docs/implementation-guide/background\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://extension.js.org/#organization\",\"name\":\"Extension.js\",\"url\":\"https://extension.js.org\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/extensionjs/OSsaYW6REekgcRaD/logo-light.png?fit=max\u0026auto=format\u0026n=OSsaYW6REekgcRaD\u0026q=85\u0026s=0eebd66d9a894dd11e16686f9ef4a875\"}},{\"@type\":\"WebSite\",\"@id\":\"https://extension.js.org#website\",\"name\":\"Extension.js\",\"url\":\"https://extension.js.org\",\"publisher\":{\"@id\":\"https://extension.js.org/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://extension.js.org/docs/implementation-guide/background#webpage\",\"url\":\"https://extension.js.org/docs/implementation-guide/background\",\"name\":\"Background scripts and MV3 service workers\",\"description\":\"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\",\"dateModified\":\"2026-09-17T03:41:32.063Z\",\"isPartOf\":{\"@id\":\"https://extension.js.org#website\"},\"breadcrumb\":{\"@id\":\"https://extension.js.org/docs/implementation-guide/background#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://extension.js.org/docs/implementation-guide/background#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Implementation guide\",\"item\":\"https://extension.js.org/docs/implementation-guide\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Background scripts and MV3 service workers\",\"item\":\"https://extension.js.org/docs/implementation-guide/background\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://extension.js.org/docs/implementation-guide/background#article\",\"headline\":\"Background scripts and MV3 service workers\",\"name\":\"Background scripts and MV3 service workers\",\"description\":\"Run extension logic with MV2 background scripts or MV3 service workers. Extension.js compiles entries from manifest.json and applies reload behavior.\",\"url\":\"https://extension.js.org/docs/implementation-guide/background\",\"mainEntityOfPage\":{\"@id\":\"https://extension.js.org/docs/implementation-guide/background#webpage\"},\"image\":\"https://media.extension.land/brand/extension-js/social/og.png\",\"dateModified\":\"2026-09-17T03:41:32.063Z\",\"keywords\":\"extension.js, extensions, firefox, browser, manifest, javascript, hmr, content script\",\"publisher\":{\"@id\":\"https://extension.js.org/#organization\"},\"isPartOf\":{\"@id\":\"https://extension.js.org#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Extension.js\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Extension.js\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/docs/implementation-guide/background.md","siteName":"Extension.js","nav":{"global":null,"languages":[{"language":"en","default":true,"tabs":[{"tab":"Home","pages":[{"title":"Home","href":""}]},{"tab":"Documentation","groups":[{"group":"Introduction","pages":[{"title":"What is Extension.js? An introduction","href":"/docs"}]},{"group":"Browser extension fundamentals","pages":[{"title":"What is a browser extension?","href":"/docs/concepts/what-is-a-browser-extension"},{"title":"Manifest V3 service workers and permissions","sidebarTitle":"Manifest V3","href":"/docs/concepts/manifest-v3"},{"title":"JavaScript and TypeScript extension files","href":"/docs/concepts/javascript-typescript-browser-extension-files"},{"title":"Session artifacts: where dev state lives","href":"/docs/concepts/session-artifacts"},{"title":"Reading Extension.js CLI output","href":"/docs/concepts/reading-cli-output"}]},{"group":"Getting started","pages":[{"title":"Getting started with Extension.js","href":"/docs/getting-started/index"},{"title":"Get started with Extension.js samples","href":"/docs/getting-started/immediately"},{"title":"Create your first browser extension","href":"/docs/getting-started/create-your-first-extension"}]},{"group":"Templates","pages":[{"title":"Extension templates for React, Vue, and more","href":"/docs/getting-started/templates"}]},{"group":"Compare and migrate","pages":[{"title":"Browser extension framework comparison","href":"/docs/compare/index"},{"title":"Extension.js vs WXT","href":"/docs/compare/extension-js-vs-wxt"},{"title":"Extension.js vs Plasmo","href":"/docs/compare/extension-js-vs-plasmo"},{"title":"Extension.js vs CRXJS","href":"/docs/compare/extension-js-vs-crxjs"},{"title":"Migrate from WXT to Extension.js","href":"/docs/migrate/from-wxt"},{"title":"Migrate from CRXJS to Extension.js","href":"/docs/migrate/from-crxjs"},{"title":"Fix CRXJS Content script fileName undefined","href":"/docs/migrate/crxjs-content-script-filename-undefined"},{"title":"Migrate from Plasmo to Extension.js","href":"/docs/migrate/from-plasmo"},{"title":"Migrate from webpack or Vite","href":"/docs/migrate/from-webpack-or-vite"}]},{"group":"Commands reference","pages":[{"title":"Extension.js command guide","href":"/docs/commands/index"},{"title":"Create command to scaffold extension projects","href":"/docs/commands/create"},{"title":"Dev command for watch mode and live reload","href":"/docs/commands/dev"},{"title":"Start command for build-and-launch workflow","href":"/docs/commands/start"},{"title":"Preview command to launch built extensions","href":"/docs/commands/preview"},{"title":"Build command for production extensions","href":"/docs/commands/build"},{"title":"Publish command for shareable build URLs","href":"/docs/commands/publish"},{"title":"Install command for managed browser runtimes","href":"/docs/commands/install"},{"title":"Uninstall command to remove managed browsers","href":"/docs/commands/uninstall"},{"title":"Doctor command for diagnosing dev sessions","href":"/docs/commands/doctor"},{"title":"Logs command for reading dev session output","href":"/docs/commands/logs"},{"title":"Inspect command for DOM and tab discovery","href":"/docs/commands/inspect"},{"title":"Eval command for extension contexts","href":"/docs/commands/eval"},{"title":"Storage command for reading and writing data","href":"/docs/commands/storage"},{"title":"Reload command for extension contexts","href":"/docs/commands/reload"},{"title":"Capabilities command for engine handshakes","href":"/docs/commands/capabilities"}]},{"group":"Features","pages":[{"title":"Extension.js features overview","href":"/docs/features/index"},{"title":"Cross-browser extension development","href":"/docs/features/cross-browser-compatibility"},{"title":"Browser and framework compatibility matrix","href":"/docs/features/compatibility-matrix"},{"title":"Browser-specific manifest fields","href":"/docs/features/browser-specific-fields"},{"title":"Special folders for pages and scripts","href":"/docs/features/special-folders"},{"title":"Environment variables in browser extensions","href":"/docs/features/environment-variables"},{"title":"Extension config (extension.config.js)","href":"/docs/features/extension-configuration"},{"title":"Rspack configuration in extension.config.js","href":"/docs/features/rspack-configuration"},{"title":"Page reload and hot module replacement (HMR)","href":"/docs/features/reload-and-hmr"},{"title":"Predictable path resolution","href":"/docs/features/path-resolution"},{"title":"Module aliases and import resolution","href":"/docs/features/module-aliases"},{"title":"Multi-platform builds for Chrome and Firefox","href":"/docs/features/multi-platform-builds"},{"title":"Telemetry and privacy controls","href":"/docs/features/telemetry-and-privacy"}]},{"group":"Browsers","pages":[{"title":"Browser targeting for Chrome, Firefox, Edge","href":"/docs/browsers/index"},{"title":"Browsers supported by Extension.js","href":"/docs/browsers/browsers-available"},{"title":"Firefox and Gecko runtime preferences setup","href":"/docs/browsers/browser-preferences"},{"title":"Browser launch flags for Chrome, Firefox, and Edge","href":"/docs/browsers/browser-flags"},{"title":"Browser profiles for isolated dev runs","href":"/docs/browsers/browser-profile"},{"title":"Running other browsers from binary path","href":"/docs/browsers/running-other-browsers"},{"title":"Developing extensions under WSL","href":"/docs/browsers/wsl"},{"title":"Building Safari extensions with Extension.js","href":"/docs/browsers/safari"}]},{"group":"Languages and frameworks","pages":[{"title":"Languages and frameworks guide","href":"/docs/languages-and-frameworks/index"},{"title":"WebAssembly in browser extensions","href":"/docs/languages-and-frameworks/webassembly"},{"title":"TypeScript for browser extensions","href":"/docs/languages-and-frameworks/typescript"},{"title":"Types for the chrome and browser APIs","href":"/docs/languages-and-frameworks/extension-api-types"},{"title":"ECMAScript modules in extensions","href":"/docs/languages-and-frameworks/ecmascript-modules"},{"title":"React for browser extensions","href":"/docs/languages-and-frameworks/react"},{"title":"Preact for browser extensions","href":"/docs/languages-and-frameworks/preact"},{"title":"Vue.js for browser extensions","href":"/docs/languages-and-frameworks/vue"},{"title":"Svelte for browser extensions","href":"/docs/languages-and-frameworks/svelte"},{"title":"CSS Modules in browser extensions","href":"/docs/languages-and-frameworks/css-modules"},{"title":"Less CSS in browser extensions","href":"/docs/languages-and-frameworks/less"},{"title":"Sass and SCSS Modules in browser extensions","href":"/docs/languages-and-frameworks/sass"},{"title":"Node APIs in browser extensions","href":"/docs/languages-and-frameworks/node"},{"title":"Deno projects with Extension.js","href":"/docs/languages-and-frameworks/deno"},{"title":"Bun projects with Extension.js","href":"/docs/languages-and-frameworks/bun"}]},{"group":"Integrations","pages":[{"title":"Integrations for Tailwind, ESLint, and more","href":"/docs/integrations/index"},{"title":"Babel integration for custom transforms","href":"/docs/integrations/babel"},{"title":"Tailwind CSS in browser extensions","href":"/docs/integrations/tailwindcss"},{"title":"UnoCSS in browser extensions","href":"/docs/integrations/unocss"},{"title":"shadcn/ui components in extensions","href":"/docs/integrations/shadcnui"},{"title":"ESLint integration for code quality","href":"/docs/integrations/eslint"},{"title":"Prettier for browser extensions","href":"/docs/integrations/prettier"},{"title":"PostCSS in browser extensions","href":"/docs/integrations/postcss"},{"title":"Stylelint for browser extension CSS","href":"/docs/integrations/stylelint"},{"title":"Biome for browser extensions","href":"/docs/integrations/biome"},{"title":"extension.dev MCP server","href":"/docs/integrations/extension-mcp"},{"title":"Chrome DevTools MCP with Extension.js","href":"/docs/integrations/chrome-devtools-mcp"}]},{"group":"Workflows","pages":[{"title":"Extension development workflows","href":"/docs/workflows/index"},{"title":"Global CLI flags for all commands","href":"/docs/workflows/global-flags"},{"title":"Dev mode update and reload behavior","href":"/docs/workflows/dev-update-behavior"},{"title":"Troubleshooting Extension.js issues","href":"/docs/workflows/troubleshooting"},{"title":"Extension security checklist","href":"/docs/workflows/security-checklist"},{"title":"Extension performance playbook","href":"/docs/workflows/performance-playbook"},{"title":"Playwright E2E testing for extensions","href":"/docs/workflows/playwright-e2e"},{"title":"CI templates for extension builds","href":"/docs/workflows/ci-templates"},{"title":"Develop in a dev container or Codespace","href":"/docs/workflows/dev-containers"},{"title":"Driving the CLI from an AI agent","href":"/docs/workflows/driving-the-cli"},{"title":"Store metadata in one STORE.md file","href":"/docs/workflows/store-metadata"},{"title":"Build an ad blocker","href":"/docs/workflows/build-an-ad-blocker"},{"title":"Intercept network requests","href":"/docs/workflows/intercept-network-requests"},{"title":"Route traffic through a proxy","href":"/docs/workflows/route-traffic-through-a-proxy"},{"title":"Manage downloads","href":"/docs/workflows/manage-downloads"},{"title":"Record the screen","href":"/docs/workflows/record-the-screen"},{"title":"Send analytics from your extension","href":"/docs/workflows/send-analytics-events"}]},{"group":"Machine contracts","pages":[{"title":"Machine contracts overview","href":"/docs/contracts/index"},{"title":"ready.json session contract","href":"/docs/contracts/ready-json"},{"title":"Result envelope (schema 1)","href":"/docs/contracts/result-envelope"},{"title":"Extension.js CLI error codes (E_* reference)","href":"/docs/contracts/error-codes"},{"title":"Lifecycle stream for dev sessions","href":"/docs/contracts/lifecycle-stream"},{"title":"Control bridge wire contract","href":"/docs/contracts/control-bridge"}]},{"group":"Publish to the stores","pages":[{"title":"Publish to the browser stores","href":"/docs/publishing/index"},{"title":"Package an extension for store upload","href":"/docs/publishing/package-for-the-stores"},{"title":"Get your Chrome Web Store credentials","href":"/docs/publishing/chrome-credentials"},{"title":"Get your Firefox Add-ons credentials","href":"/docs/publishing/firefox-credentials"},{"title":"Get your Edge Add-ons credentials","href":"/docs/publishing/edge-credentials"}]},{"group":"Debugging","pages":[{"title":"Debug a browser extension from the terminal","href":"/docs/debugging/index"},{"title":"Trigger toolbar actions and commands","href":"/docs/debugging/trigger-actions-and-commands"},{"title":"Manifest refusals: why Chromium won't load","href":"/docs/debugging/manifest-refusals"},{"title":"Source maps for extension debugging","href":"/docs/debugging/source-maps"}]},{"group":"Implementation guide","pages":[{"title":"Extension implementation guide","href":"/docs/implementation-guide/index"},{"title":"Manifest.json compilation and output","href":"/docs/implementation-guide/manifest-json"},{"title":"Permissions and host permissions","href":"/docs/implementation-guide/permissions-and-host-permissions"},{"title":"Message passing between extension contexts","href":"/docs/implementation-guide/messaging"},{"title":"The tabs API in development and production","href":"/docs/implementation-guide/tabs"},{"title":"Native messaging with local applications","href":"/docs/implementation-guide/native-messaging"},{"title":"CORS and cross-origin requests","href":"/docs/implementation-guide/cross-origin-requests"},{"title":"Extension storage: local, sync, and managed","href":"/docs/implementation-guide/storage"},{"title":"HTML entrypoints for extension pages","href":"/docs/implementation-guide/html"},{"title":"Popup sizing: dimensions and CSS patterns","href":"/docs/implementation-guide/popup-sizing"},{"title":"Side panel and sidebar in Chrome and Firefox","href":"/docs/implementation-guide/side-panel"},{"title":"CSS, Sass, and Less in browser extensions","href":"/docs/implementation-guide/css"},{"title":"JavaScript and TypeScript in extensions","description":"Ship JavaScript and TypeScript across background, content scripts, and UI pages with one Extension.js pipeline powered by Rspack and SWC transforms.","href":"/docs/implementation-guide/javascript"},{"title":"Background scripts and MV3 service workers","href":"/docs/implementation-guide/background"},{"title":"Content scripts with HMR and manifest entries","description":"Build page-integrated features with content scripts. Extension.js compiles manifest entries, wraps them for HMR, and emits predictable outputs.","href":"/docs/implementation-guide/content-scripts"},{"title":"Shadow DOM in content scripts","href":"/docs/implementation-guide/shadow-dom"},{"title":"Inject scripts at runtime","href":"/docs/implementation-guide/inject-scripts"},{"title":"Offscreen documents in Manifest V3","href":"/docs/implementation-guide/offscreen-documents"},{"title":"Web Workers in a content script","href":"/docs/implementation-guide/web-workers"},{"title":"Sandbox pages and the sandbox manifest key","href":"/docs/implementation-guide/sandbox-pages"},{"title":"Lazy loading heavy libraries in extensions","href":"/docs/implementation-guide/lazy-loading"},{"title":"Locales and internationalization","href":"/docs/implementation-guide/locales"},{"title":"JSON resources in extensions","href":"/docs/implementation-guide/json"},{"title":"Extension icons and manifest declarations","href":"/docs/implementation-guide/icons"},{"title":"Browser themes in Extension.js","href":"/docs/implementation-guide/themes"},{"title":"Make an extension file readable by a web page","href":"/docs/implementation-guide/web-accessible-resources"}]},{"group":"Community","pages":[{"title":"Extension.js roadmap","href":"/docs/roadmap"},{"title":"Extension.js hall of fame and contributors","href":"/docs/hall-of-fame"}]}]},{"tab":"Templates","href":"https://templates.extension.dev/?utm_source=extension-js-org\u0026utm_campaign=nav-templates"},{"tab":"Showcase","pages":[{"title":"Showcase","href":"/showcase"}]},{"tab":"AI","pages":[{"title":"AI","href":"/docs/ai-access"}]},{"tab":"Blog","pages":[{"title":"Blog","href":"/blog"}]}]},{"language":"zh-Hans","pages":[{"title":"zh-Hans","href":"/zh-Hans/docs/implementation-guide/background"}]},{"language":"zh-Hant","pages":[{"title":"zh-Hant","href":"/zh-Hant/docs/implementation-guide/background"}]}]},"basePath":"","title":"Background scripts and MV3 service workers - Extension.js","buildId":"dpl_596ePT575z4qnAFksq1biRW97aLY","deploymentId":"dpl_596ePT575z4qnAFksq1biRW97aLY","dynamics":["a74be98ba7"],"chunks":["zoom-image-lightbox-C5F0mVLH.js","lightbox-handle-r17bVUz3.js"]}