---
url: /frameworks/sveltekit.md
---

# SvelteKit

::: tip
From version `^0.6.7`, `SvelteKitPWA` adds support for [Single-page apps](https://svelte.dev/docs/kit/single-page-apps): check [SPA](#spa) section for more information.
:::

::: tip
From version `^0.1.0`, `SvelteKitPWA` has SvelteKit `^1.0.0` as peer dependency.
:::

::: info
For `Type declarations`, `Prompt for update` and `Periodic SW Updates` go to [Svelte](/frameworks/svelte) entry.
:::

::: tip
If you're using `0.1.*` version of `SvelteKitPWA`, you should remove all references to [SvelteKit service worker module](https://kit.svelte.dev/docs/service-workers) to disable it on your application.
:::

## Installing @vite-pwa/sveltekit

To install the `@vite-pwa/sveltekit` plugin, just add it to your project as a `dev dependency`:
::: code-group

```bash [pnpm]
pnpm add -D @vite-pwa/sveltekit
```

```bash [yarn]
yarn add -D @vite-pwa/sveltekit
```

```bash [npm]
npm install -D @vite-pwa/sveltekit
```

:::

## Workbox Configuration

### globPatterns

`@vite-pwa/sveltekit` configures the following `globPatterns` for you (`workbox` or `injectManifest` option depending on the strategy configured):

* `workbox: { globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,webmanifest}', 'prerendered/**/*.{html,json}'] }` or
* `injectManifest: { globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,webmanifest}', 'prerendered/**/*.{html,json}'] }`

`@vite-pwa/sveltekit` configures the `.svelte-kit/output` directory as the `globDirectory` for the `workbox-build` process (`workbox` or `injectManifest` option depending on the strategy configured). This directory will contain all the files generated by SvelteKit when building your application as an intermediate step before the final adapter build. This directory will have the following structure:

* `client` directory: will contain all the client side files generated by SvelteKit (`.js`, `.css`) and all assets files in the static directory (`.ico`, `.png`, `.svg`, `.webp`).
* `prerendered/pages` directory: will contain all the prerendered pages (`.html`).
* `prerendered/dependencies/<page>/__data.json` files: `load` functions payload when using `static-adapter`.
* `server` directory: will contain all the server side files generated by SvelteKit (`.js`, `.css`).

If you want to add some extra files to the `globPatterns` configuration, remember to include the glob adding the `client/` prefix, or you will end up including server assets in the service worker precache manifest and your application will fail when registering the service worker.

If you want to add the SvelteKit `_app/version.json` file to your service worker precache manifest, enable the `kit.includeVersionFile` option in your PWA configuration.

## Generate Custom Service Worker

From version `0.2.0`, `SvelteKitPWA` plugin will delegate your custom service worker build to SvelteKit, and so by default you will be expected to put your service worker in `src/service-worker.js`. If you would like, you can use a custom file location by changing the corresponding SvelteKit option:

```js
// svelte.config.js
/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    files: {
      serviceWorker: 'src/my-sw.js', // or `src/my-sw.ts`
    }
  }
};

export default config;
```

Then in your Vite config file:

```js
// vite.config.js or vite.config.ts
/** @type {import('vite').UserConfig} */
const config = {
  plugins: [
    sveltekit(),
    SvelteKitPWA({
      strategies: 'injectManifest',
      srcDir: 'src',
      filename: 'my-sw.js', // or `my-sw.ts`
      /* other pwa options */
    })
  ],
};

export default config;
```

You can check SvelteKit docs for more information about [service workers](https://kit.svelte.dev/docs/service-workers).

You will need to exclude the service worker registration from the SvelteKit configuration if you're using any pwa virtual module (`virtual:pwa-register` or `virtual:pwa-register/svelte`):

```js
// svelte.config.js
/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    serviceWorker: {
      register: false
    }
  }
};

export default config;
```

::: warning
If your custom service working is importing any `workbox-*` module (`workbox-routing`, `workbox-strategies`, etc), you will need to hack Vite build process in order to remove non `ESM` special replacements from the build process (if you don't include `process.env.NODE_ENV`, the service worker will not be registered). You only need to add this entry in your Vite config file:

```js
// vite.config.js or vite.config.ts
/** @type {import('vite').UserConfig} */
const config = {
  define: {
    'process.env.NODE_ENV': process.env.NODE_ENV === 'production'
      ? '"production"'
      : '"development"'
  }
};

export default config;
```

:::

## SvelteKit PWA Plugin

`vite-plugin-pwa` provides the new `SvelteKitPWA` plugin that will allow you to use `vite-plugin-pwa` in your SvelteKit applications.

To update your project to use the new `vite-plugin-pwa` for SvelteKit, you only need to change the Vite config file (you don't need oldest `pwa` and `pwa-configuration` modules):

```js
// vite.config.js / vite.config.ts
import { SvelteKitPWA } from '@vite-pwa/sveltekit'

/** @type {import('vite').UserConfig} */
const config = {
  plugins: [
    sveltekit(),
    SvelteKitPWA({/* pwa options */})
  ],
}

export default config
```

In addition to the configuration above, it's necessary to add the PWA web manifest, currently the easiest way to do this, is to add it to any layout to your kit project:

```svelte
// src/routes/+layout.svelte
<script>
  import { pwaInfo } from 'virtual:pwa-info';

  $: webManifestLink = pwaInfo ? pwaInfo.webManifest.linkTag : ''
</script>

<svelte:head>
  {@html webManifestLink}
</svelte:head>
```

Check out the [virtual:pwa-info](/frameworks/#accessing-pwa-info) documentation to learn more about the virtually exposed module `pwa-info`.

## SvelteKit PWA Plugin Options

::: details SvelteKit PWA Plugin options

```ts
import type { VitePWAOptions } from 'vite-plugin-pwa'

export interface KitOptions {
  /**
   * The base path for your application: by default will use the Vite base.
   *
   * @deprecated since ^0.1.0 version, the plugin has SvelteKit ^1.0.0 as peer dependency, Vite's base is now properly configured.
   * @default '/'
   * @see https://kit.svelte.dev/docs/configuration#paths
   */
  base?: string

  /**
   * @default '.svelte-kit'
   * @see https://kit.svelte.dev/docs/configuration#outdir
   */
  outDir?: string

  /**
   * @see https://github.com/sveltejs/kit/tree/master/packages/adapter-static#fallback
   */
  adapterFallback?: string

  /**
   * @default 'never'
   * @see https://kit.svelte.dev/docs/configuration#trailingslash
   */
  trailingSlash?: 'never' | 'always' | 'ignore'

  /**
   * @default `_app`
   * @see https://kit.svelte.dev/docs/configuration#appdir
   */
  appDir?: string

  /**
   * Include `${appDir}/version.json` in the service worker precache manifest?
   *
   * @default false
   */
  includeVersionFile?: boolean

  /**
   * Enable SPA mode for the application.
   *
   * By default, the plugin will use `adapterFallback` to include the entry in the service worker
   * precache manifest.
   *
   * If you are using a logical name for the fallback, you can use the object syntax with the
   * `fallbackMapping`.
   *
   * For example, if you're using `fallback: 'app.html'` in your static adapter and your server
   * is redirecting to `/app`, you can configure `fallbackMapping: '/app'`.
   *
   * Since the static adapter will run after the PWA plugin generates the service worker,
   * the PWA plugin doesn't have access to the adapter fallback page to include the revision in the
   * service worker precache manifest.
   * To generate the revision for the fallback page, the PWA plugin will use the
   * `.svelte-kit/output/client/_app/version.json` file.
   * You can configure the `fallbackRevision` to generate a custom revision.
   *
   * @see https://svelte.dev/docs/kit/single-page-apps
   */
  spa?: true | {
    fallbackMapping?: string
    fallbackRevision?: () => Promise<string>
  }
}

export interface SvelteKitPWAOptions extends Partial<VitePWAOptions> {
  kit?: KitOptions
}
```

:::

## SvelteKit Pages

If you want your application to work offline, you should ensure you have not set `csr: false` on any of your pages since it will prevent injecting JavaScript into the layout for offline support.

### Auto Update

Since SvelteKit uses SSR/SSG, we need to call the `vite-plugin-pwa` virtual module using a dynamic `import`.

The best place to include the virtual call will be in main layout of the application (you should register it in any layout):

::: details src/routes/+layout.svelte

```svelte
<script>
  import { onMount } from 'svelte'
  import { pwaInfo } from 'virtual:pwa-info'

  onMount(async () => {
    if (pwaInfo) {
      const { registerSW } = await import('virtual:pwa-register')
      registerSW({
        immediate: true,
        onRegistered(r) {
          // uncomment following code if you want check for updates
          // r && setInterval(() => {
          //    console.log('Checking for sw update')
          //    r.update()
          // }, 20000 /* 20s for testing purposes */)
          console.log(`SW Registered: ${r}`)
        },
        onRegisterError(error) {
          console.log('SW registration error', error)
        }
      })
    }
  })

  $: webManifest = pwaInfo ? pwaInfo.webManifest.linkTag : ''
</script>

<svelte:head>
    {@html webManifest}
</svelte:head>

<main>
  <slot />
</main>
```

:::

### Prompt for update

Since SvelteKit uses SSR/SSG, we need to add the `ReloadPrompt` component using a dynamic `import`.

The best place to include the `ReloadPrompt` component will be in main layout of the application (you should register it in any layout):

::: details src/routes/+layout.svelte

```html
<script>
  import { pwaInfo } from 'virtual:pwa-info'

  $: webManifest = pwaInfo ? pwaInfo.webManifest.linkTag : ''
</script>

<svelte:head>
    {@html webManifest}
</svelte:head>

<main>
  <slot />
</main>

{#await import('$lib/ReloadPrompt.svelte') then { default: ReloadPrompt}}
  <ReloadPrompt />
{/await}
```

:::

::: details $lib/ReloadPrompt.svelte

```html
<script lang="ts">
  import { useRegisterSW } from 'virtual:pwa-register/svelte'
  const {
    needRefresh,
    updateServiceWorker,
    offlineReady
  } = useRegisterSW({
    onRegistered(r) {
      // uncomment following code if you want check for updates
      // r && setInterval(() => {
      //   console.log('Checking for sw update')
      //   r.update()
      // }, 20000 /* 20s for testing purposes */)
      console.log(`SW Registered: ${r}`)
    },
    onRegisterError(error) {
      console.log('SW registration error', error)
    },
  })
  const close = () => {
    offlineReady.set(false)
    needRefresh.set(false)
  }
  $: toast = $offlineReady || $needRefresh
</script>

{#if toast}
<div class="pwa-toast" role="alert">
  <div class="message">
    {#if $offlineReady}
      <span>
        App ready to work offline
      </span>
    {:else}
      <span>
        New content available, click on reload button to update.
      </span>
    {/if}
  </div>
  {#if $needRefresh}
    <button on:click={() => updateServiceWorker(true)}>
      Reload
    </button>
  {/if}
  <button on:click={close}>
    Close
  </button>
</div>
{/if}

<style>
.pwa-toast {
  position: fixed;
  right: 0;
  bottom: 0;
  margin: 16px;
  padding: 12px;
  border: 1px solid #8885;
  border-radius: 4px;
  z-index: 2;
  text-align: left;
  box-shadow: 3px 4px 5px 0 #8885;
  background-color: white;
}
.pwa-toast .message {
  margin-bottom: 8px;
}
.pwa-toast button {
  border: 1px solid #8885;
  outline: none;
  margin-right: 5px;
  border-radius: 2px;
  padding: 3px 10px;
}
</style>
```

:::

## SvelteKit and Adapters

If you set certain SvelteKit options, you should also configure the PWA plugin properly using the `kit` option:

* [outDir](https://kit.svelte.dev/docs/configuration#outdir)
* [adapterFallback](https://github.com/sveltejs/kit/tree/master/packages/adapter-static#fallback)
* [trailingSlash](https://kit.svelte.dev/docs/configuration#trailingslash)

::: warning
Some kit options may have been moved/deprecated, review the SvelteKit documentation site:

* [trailingSlash](https://kit.svelte.dev/docs/page-options#trailingslash): now it should be configured in the page options, and so, we cannot control it in the plugin.
  :::

### SPA

If you are using SvelteKit SPA mode, the `static-adapter` will create the fallback after `@vite-pwa/sveltekit` plugin generates the service worker, and so the plugin doesn't have access to the adapter fallback page to include the revision in the service worker precache manifest.

To generate the revision for the fallback page, the plugin will use the `.svelte-kit/output/client/_app/version.json` file.

You can configure the `spa.fallbackRevision` function to generate a custom revision.

## PWA Assets &#x20;

We suggest you using external configuration file, `@vite-pwa/sveltekit` plugin will watch it for changes, avoiding dev server restarts. If you use inlined configuration, Vite will restart the dev server when changing any option.

To inject the PWA icons links and the `theme-color`, you can use the `virtual:pwa-assets/head` virtual module in your `+layout.svelte` component:

* add `import 'vite-plugin-pwa/pwa-assets';` to your `src/app.d.ts` file
* remove all links with rel `icon`, `apple-touch-icon` and `apple-touch-startup-image` from `<svelte:head>` or from your `app.html` file
* remove the `theme-color` meta tag from `<svelte:head>` or from your `app.html` file
* add the virtual import
* include theme color and icons links using code-snippet shown below

```html
<script>
import { pwaAssetsHead } from 'virtual:pwa-assets/head';
</script>

<svelte:head>
  {#if pwaAssetsHead.themeColor}
  <meta name="theme-color" content={pwaAssetsHead.themeColor.content} />
  {/if}
  {#each pwaAssetsHead.links as link}
  <link {...link} />
  {/each}
</svelte:head>
```

You can find a working example in the [examples folder](https://github.com/vite-pwa/sveltekit/tree/main/examples/sveltekit-ts-assets-generator).
