Where dynamic import() works
The platform rules, before any bundler enters the picture:
| Context | Dynamic import() at runtime |
|---|---|
| Service worker (background) | No, the spec disallows it |
| Extension pages (popup, options, new tab, side panel) | Yes |
| Offscreen documents | Yes |
| Content scripts | Yes, with one extra step |
import() is unavailable inside service workers at the platform level, in any browser. Static import statements at the top of the worker are fine (Extension.js bundles them), but you cannot defer a chunk load until an event fires.
Pattern 1: split by surface, not by chunk
The service worker should orchestrate, not compute. Keep its static imports to glue code (routing messages, registering listeners) and move anything heavy to a surface where lazy loading works:- UI-adjacent work belongs in the page that needs it. A popup that formats code imports the formatter in the popup, not the worker.
- Background-only heavy work (parsing, encoding, inference) belongs in an offscreen document, created on demand and closed when done. The worker stays a thin message router.
background.ts
import() the heavy library the first time it is asked, and the cost is paid once per document lifetime instead of once per worker wake-up.
Pattern 2: lazy import inside extension pages
Inside any extension page, plain dynamic import works and the bundler splits the chunk automatically:popup.ts
Pattern 3: lazy import in content scripts
Content scripts execute in the page, so their lazily loaded chunks must be fetchable by that page. Two steps:- Expose the chunk paths via
web_accessible_resources. - Import through
chrome.runtime.getURLso the URL resolves to your extension origin:
content.ts
What to keep out of the worker entirely
- Anything DOM-shaped (
DOMParser, canvas, audio): the worker has no DOM at all; use an offscreen document. - Multi-megabyte wasm: instantiate it in an offscreen document or an extra page, and cache results in
chrome.storageso restarts are cheap. - One-off tooling (imports, migrations, exports): a dedicated page under
pages/keeps it out of every startup path.

