Service workers get ES module support

ES modules have long been the standard way to share JavaScript code across the web platform. Their modular, statically analyzable structure makes them easier to maintain and debug than older script-loading patterns. But one notable gap has persisted: until recently, service workers could not use them.

Service workers still rely primarily on the classic importScripts() API for pulling in external code. That's now changing, as browser vendors have begun shipping ES module support inside service workers. Here's a look at where that support stands, what limitations remain, and how to structure code that works both in modern browsers and legacy ones.

Why ES modules matter in service workers

The main practical benefit is code sharing. With ES modules, shared logic and configuration can live in one file and be imported by both the window context and the service worker, without resorting to legacy universal module formats like UMD or polluting global scope.

There is also an update-behavior advantage. Modules imported into a service worker participate in the same update flow as scripts loaded via importScripts(): if the contents of an imported module change, the browser detects the change and triggers a service worker update.

What's still missing

Support is not yet complete, and two significant restrictions remain.

Static imports only

Within a service worker, you can only use the static import ... from '...' syntax. Dynamic imports via import() are not allowed.

This mirrors an existing constraint on importScripts(), which must be called synchronously and must finish before the service worker's install phase completes. That requirement exists so the browser can identify and cache every piece of JavaScript the service worker depends on during installation. Although there is discussion about allowing dynamic imports in the future, for now any service worker code must use static imports exclusively.

Other worker types are further along

Dedicated workers (new Worker('...', {type: 'module'})) have had module support for longer. Chrome and Edge added it in version 80, and recent Safari versions support it too. Dedicated workers also allow dynamic imports, which sets them apart from service workers.

Shared workers have module support in Chrome and Edge since version 83, but no other browser has implemented it yet.

No import maps

Import maps—a mechanism for remapping module specifiers (for example, to point at a preferred CDN)—are not usable in service workers. Chrome and Edge have supported import maps in regular pages since version 89, but that support does not extend to service workers.

Browser compatibility

Support for ES modules in service workers began rolling out in Chrome and Edge with version 91. Safari has added it in Technology Preview builds, and it is expected to land in a future stable release.

Building with ES modules

The basic pattern is straightforward when your browser supports it. You create one ES module that holds shared logic, and import it from both the page and the service worker via import ... from './shared-module.js'. The service worker is then registered with navigator.serviceWorker.register('/sw.js', {type: 'module'}), using the same {type: 'module'} option you'd pass to new Worker().

Serving older browsers

Not all browsers have this support yet, so you can't ship a single module-based service worker to everyone. Two approaches can handle the gap.

First, use a bundler that understands ES modules. Run the service worker source through it and emit a single script with all module code inlined. That bundled file — which contains no import statements — runs in browsers without module support.

Second, if the code you want is already published as an IIFE or UMD bundle, load it with importScripts() in the classic service worker variant instead.

With both versions in hand, you need a way to pick the right one at registration time. Best practices for feature-detecting module support inside service workers are still evolving; following the discussion in the relevant Service Worker specification issue is the most reliable way to stay current.