Skip to content

The $service-worker Module

SvelteKit automatically bundles and registers any file you place at src/service-worker.js (or .ts). Inside that file, the $service-worker virtual module gives you everything you need to write a robust precaching service worker without any third-party library.

ExportTypeContents
buildstring[]Hashed client-side assets (_app/immutable/…)
filesstring[]Everything copied from static/
versionstringUnique string that changes on every build
prerenderedstring[]Paths of prerendered HTML pages

build and files together cover the full set of assets your app needs to work offline. version makes a unique cache name so old caches are cleaned up reliably after each deploy.

/// <reference types="@sveltejs/kit" />
import { build, files, version, prerendered } from '$service-worker';
const CACHE = `cache-\${version}`;
const ASSETS = [
...build, // hashed JS/CSS bundles
...files, // static/ assets (icons, fonts, …)
...prerendered, // prerendered HTML pages
];
// -- Install: precache all assets ----------------------------------------------
self.addEventListener('install', (event) => {
async function addFilesToCache() {
const cache = await caches.open(CACHE);
await cache.addAll(ASSETS);
}
event.waitUntil(addFilesToCache());
});
// -- Activate: delete old caches -----------------------------------------------
self.addEventListener('activate', (event) => {
async function deleteOldCaches() {
for (const key of await caches.keys()) {
if (key !== CACHE) await caches.delete(key);
}
}
event.waitUntil(deleteOldCaches());
});
// -- Fetch: serve from cache, fall back to network -----------------------------
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
async function respond() {
const url = new URL(event.request.url);
const cache = await caches.open(CACHE);
// Always serve build/files assets from cache (they are hashed)
if (ASSETS.includes(url.pathname)) {
const cached = await cache.match(url.pathname);
if (cached) return cached;
}
// For everything else: network first, cache as fallback
try {
const response = await fetch(event.request);
if (response.status === 200) {
cache.put(event.request, response.clone());
}
return response;
} catch {
const cached = await cache.match(event.request);
if (cached) return cached;
throw new Error('No cached response and network unavailable');
}
}
event.respondWith(respond());
});

Every SvelteKit build produces a fresh version string derived from the build timestamp and content hash. The pattern `cache-\${version}` means each deployment gets its own isolated cache. The activate handler then deletes every cache whose name is not the current version — automatically cleaning up storage from all previous deploys.

For TypeScript projects, rename the file to src/service-worker.ts and add the reference directive so the SW globals are typed correctly:

/// <reference types="@sveltejs/kit" />
import { build, files, version, prerendered } from '$service-worker';
declare const self: ServiceWorkerGlobalScope;
const CACHE = `cache-\${version}`;
// … rest of the file is identical

If your app prerender pages (via export const prerender = true in route files or adapter-static), SvelteKit populates prerendered with their paths. Including them in ASSETS means those pages load instantly offline, without a network round-trip.

OptionBenefitCost
Use SvelteKit’s $service-worker moduleDirect access to the build manifest (asset list, version) straight from SvelteKit itselfTied to a SvelteKit-specific convention — moving to another framework means rewriting it
Write a static service worker without relying on the SvelteKit moduleWorks even outside the SvelteKit build pipelineYou maintain the asset list yourself instead of letting the framework generate it
  • Importing $service-worker in the wrong context (outside the service worker file), causing a build error because the module only resolves when building the service worker.
  • Not using the build and files exports from this module for precaching, forcing you to list assets by hand and fall out of sync with the real build.
  • Forgetting that version from this module changes on every build, so if you do not use it in the cache name, old caches get invalidated at the wrong time.

The SvelteKit official service worker docs present $service-worker as the standard approach for every SvelteKit project that needs build-aware precaching, and many projects built on SvelteKit extend this same module to build an offline-first app.

What does the build export from $service-worker contain?
Why is the cache named using the version export?
Which event is the right place to delete stale caches from previous deployments?
Which $service-worker export lists paths to prerendered HTML pages?