Introduction

This feature integrated @vite-pwa/sveltekit

Pass pwa option to theme default to use pwa. The options are exactly the same as SvelteKit PWA Plugin Options except for darkManifest, which is the manifest path that would used for dark theme

And the svelte.config.js need to config files.serviceWorker, use the SERVICE_WORKER_PATH exported from @sveltepress/theme-default

svelte.config.js
+
+
+
+
import function adapter(options?: AdapterOptions): Adapteradapter from '@sveltejs/adapter-static'
import { function vitePreprocess(opts?: VitePreprocessOptions): PreprocessorGroupvitePreprocess } from '@sveltejs/vite-plugin-svelte'

import { const SERVICE_WORKER_PATH: stringSERVICE_WORKER_PATH } from '@sveltepress/theme-default' 

/** @type {import('@sveltejs/kit').Config} */
const 
const config: {
    extensions: string[];
    preprocess: PreprocessorGroup[];
    kit: {
        adapter: Adapter;
        files: {
            serviceWorker: string;
        };
    };
}
@type{import('@sveltejs/kit').Config}
config
= {
extensions: string[]extensions: ['.svelte', '.md'], preprocess: PreprocessorGroup[]preprocess: [function vitePreprocess(opts?: VitePreprocessOptions): PreprocessorGroupvitePreprocess()],
kit: {
    adapter: Adapter;
    files: {
        serviceWorker: string;
    };
}
kit
: {
adapter: Adapteradapter: function adapter(options?: AdapterOptions): Adapteradapter(),
files: {
    serviceWorker: string;
}
files
: {
serviceWorker: stringserviceWorker: const SERVICE_WORKER_PATH: stringSERVICE_WORKER_PATH, }, }, } export default
const config: {
    extensions: string[];
    preprocess: PreprocessorGroup[];
    kit: {
        adapter: Adapter;
        files: {
            serviceWorker: string;
        };
    };
}
@type{import('@sveltejs/kit').Config}
config
ts
package required

If you want to enable pwa. You will need to add workbox-window as a dev dependency to your Vite project.

Precache (versions & i18n)

By default Sveltepress only precaches the app shell and the homepage HTML. The shell is SvelteKit’s entry modules, hashed CSS / fonts, and root icons — not per-route _app/immutable/nodes or shared chunks. Other documentation pages and those hashed modules are cached at runtime when the user visits them (pages: NetworkFirst, capped at 50 entries; hashed client files: CacheFirst, capped at 400 entries / 30 days). Images and SvelteKit __data.json responses are also runtime-cached.

This keeps service worker install and update fast when the site has many pages, versions, and locales, so the refresh prompt can appear soon after a deploy. Precaching every prerendered HTML file or every client module makes Workbox hash, compare and download versions × locales × pages on every update.

On first install, homepage hydration may need the network until those hashed modules have been runtime-cached. After one online visit, visited pages (including home) stay available offline through the runtime cache.

pwa.precachePages

ValuePrecached HTML
false (default)Homepage only
trueAll prerendered HTML (historical versions are still ignored)
string[]Homepage + matching URL prefixes

Precache only the current locale and a version snapshot:

import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'

function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
  
DefaultThemeOptions.pwa?: (SvelteKitPWAOptions & {
    darkManifest?: string;
    precachePages?: boolean | string[];
    precacheClient?: boolean;
}) | undefined
pwa
: {
precachePages?: boolean | string[] | undefined

Which prerendered HTML pages to put in the Workbox precache.

Default false only precaches the homepage so service-worker install/update stays fast on sites with many versions and locales.

  • false: homepage only
  • true: all prerendered HTML (historical versions are still ignored)
  • string[]: URL prefixes, e.g. ['/zh/', '/v/2026-08-27/']
precachePages
: ['/zh/', '/v/2026-08-27/'],
}, })
ts

Restore the previous “cache every page” behavior:

import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'

function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
  
DefaultThemeOptions.pwa?: (SvelteKitPWAOptions & {
    darkManifest?: string;
    precachePages?: boolean | string[];
    precacheClient?: boolean;
}) | undefined
pwa
: {
precachePages?: boolean | string[] | undefined

Which prerendered HTML pages to put in the Workbox precache.

Default false only precaches the homepage so service-worker install/update stays fast on sites with many versions and locales.

  • false: homepage only
  • true: all prerendered HTML (historical versions are still ignored)
  • string[]: URL prefixes, e.g. ['/zh/', '/v/2026-08-27/']
precachePages
: true,
}, })
ts
TIP

A glob starting with prerendered/ is always included. Otherwise @vite-pwa/sveltekit would append prerendered/**/*.{html,json} and pull every version/locale page back into the precache.

Visited pages still work offline through the runtime cache, even when they are not precached.

App-shell client precache

pwa.precacheClient

ValuePrecached client files
false (default)App shell only (entry + CSS / fonts + root icons)
trueEvery matching client file

Restore the previous “precache every client JS/CSS module” behavior:

import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'

function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
  
DefaultThemeOptions.pwa?: (SvelteKitPWAOptions & {
    darkManifest?: string;
    precachePages?: boolean | string[];
    precacheClient?: boolean;
}) | undefined
pwa
: {
precacheClient?: boolean | undefined

Which client files to put in the Workbox precache.

Default false only precaches the app shell (SvelteKit entry, hashed CSS/fonts, and root icons) so service-worker install/update stays fast on sites with many pages. Per-route nodes and chunks are fetched on demand.

  • false: app shell only
  • true: every matching client file (the previous catch-all glob)
precacheClient
: true,
}, })
ts

precachePages and precacheClient are independent: HTML policy does not change the client glob, and the other way around.

Example config

Take the config this site use for example:

export default {
  scope: stringscope: '/',
  base: stringbase: '/',
  strategies: stringstrategies: 'generateSW',
  
kit: {
    trailingSlash: string;
}
kit
: {
trailingSlash: stringtrailingSlash: 'always', }, darkManifest: stringdarkManifest: '/manifest-dark.webmanifest',
manifest: {
    start_url: string;
    scope: string;
    name: string;
    short_name: string;
    icons: {
        src: string;
        sizes: string;
        type: string;
    }[];
    theme_color: string;
    background_color: string;
    display: string;
}
manifest
: {
start_url: stringstart_url: '/', scope: stringscope: '/', name: stringname: 'Sveltepress', short_name: stringshort_name: 'Sveltepress',
icons: {
    src: string;
    sizes: string;
    type: string;
}[]
icons
: [
{ src: stringsrc: '/android-chrome-192x192.png', sizes: stringsizes: '192x192', type: stringtype: 'image/png', }, { src: stringsrc: '/android-chrome-512x512.png', sizes: stringsizes: '512x512', type: stringtype: 'image/png', }, ], theme_color: stringtheme_color: '#f2f2f2', background_color: stringbackground_color: '#f2f2f2', display: stringdisplay: 'standalone', }, } as any
ts
Expand code