---
title: "Use SvelteKit with Prismic"
description: "Learn how to build websites with SvelteKit and Prismic."
category: "frameworks"
audience: developers
lastUpdated: "2026-04-28T19:34:14.000Z"
---

> This guide uses the [Type Builder](https://prismic.io/docs/type-builder.md), which is rolling out to new users. If you do not have access, use the [Slice Machine guide](https://prismic.io/docs/sveltekit/with-slice-machine.md).

# Overview

Prismic has a first-party SvelteKit integration that supports all of Prismic's features:

* Model content with [page types](https://prismic.io/docs/content-modeling.md#page-types) and [slices](https://prismic.io/docs/slices.md) using the [Type Builder](https://prismic.io/docs/type-builder.md) or the [Prismic CLI](https://prismic.io/docs/cli.md).
* Fetch and display content using [SDKs](https://prismic.io/docs/apis.md) with generated [TypeScript](https://www.typescriptlang.org/) types.
* Preview draft content with [live previews](https://prismic.io/docs/previews.md#live-previews) and [full-website previews](https://prismic.io/docs/previews.md#full-website-previews).

> <CalloutHeading>Using an AI agent?</CalloutHeading>
>
> Configure your agent to use this page as a reference.
>
> <CalloutButton href="https://prismic.io/docs/ai.md" endIcon={<ArrowRightIcon />}>
>   Set up your AI agent
> </CalloutButton>

Here's what a Prismic page looks like in SvelteKit:

```svelte filename=src/routes/[[preview=preview]]/[uid]/+page.svelte
<script lang="ts">
  import { SliceZone } from "@prismicio/svelte";
  import { components } from "$lib/slices";
  import type { PageProps } from "./$types";

  // Page data is loaded in +page.server.ts
  const { data }: PageProps = $props();
</script>

<!-- Display the page's slices -->
<SliceZone slices={data.page.data.slices} {components} />
```

SvelteKit websites use [@prismicio/client](https://prismic.io/docs/technical-reference/prismicio-client/v7.md) and [@prismicio/svelte](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md).

# Set up a SvelteKit website

Prismic can be added to new or existing SvelteKit websites. Set up your project using the [Type Builder](https://prismic.io/docs/type-builder.md), a tool for building by hand, or the [Prismic CLI](https://prismic.io/docs/cli.md), a tool for AI agents.

* **Type Builder:**

  1. **Create a Prismic repository**

     From the [Prismic dashboard](https://prismic.io/dashboard), create a Prismic repository. Select **SvelteKit**.

  2. **Set up your project**

     Follow the setup instructions shown in your Prismic repository. The instructions walk you through creating a SvelteKit project, connecting it to Prismic, and modeling content with the Type Builder.

  3. **Add `<PrismicPreview>` to your root layout**

     [`<PrismicPreview>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismicpreview) adds support for content draft previews. The `$lib/prismicio` import comes from a file that `prismic init` creates for you during setup.

     ```svelte filename=src/routes/+layout.svelte {3,4,14}
     <script lang="ts">
       import type { Snippet } from "svelte";
       import { PrismicPreview } from "@prismicio/svelte/kit";
       import { repositoryName } from "$lib/prismicio";

       type Props = {
         children: Snippet;
       };

       let { children }: Props = $props();
     </script>

     <main>{@render children()}</main>
     <PrismicPreview {repositoryName} />
     ```

     Your SvelteKit website is now ready for Prismic. Continue to [create pages](#create-pages) and [slices](#create-slices).

* **Prismic CLI:**

  > Your AI agent can perform these steps for you. See [Prismic with AI](https://prismic.io/docs/ai.md) for details.

  1. **Create a SvelteKit project**

     ```sh
     npx sv create my-website
     cd my-website
     ```

     You can also use an existing SvelteKit project.

  2. **Add Prismic to your project**

     The `init` command creates a Prismic repository, installs packages, and configures your project.

     ```sh
     npx prismic init
     ```

     If you already have a Prismic repository, provide its domain with `--repo`:

     ```sh
     npx prismic init --repo your-domain
     ```

  3. **Add `<PrismicPreview>` to your root layout**

     [`<PrismicPreview>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismicpreview) adds support for content draft previews. The `$lib/prismicio` import comes from a file that `prismic init` creates for you.

     ```svelte filename=src/routes/+layout.svelte {3,4,14}
     <script lang="ts">
       import type { Snippet } from "svelte";
       import { PrismicPreview } from "@prismicio/svelte/kit";
       import { repositoryName } from "$lib/prismicio";

       type Props = {
         children: Snippet;
       };

       let { children }: Props = $props();
     </script>

     <main>{@render children()}</main>
     <PrismicPreview {repositoryName} />
     ```

     Your SvelteKit website is now ready for Prismic. Continue to [create pages](#create-pages) and [slices](#create-slices).

# Create pages

Content writers create pages from [page types](https://prismic.io/docs/content-modeling.md#page-types), like a homepage, blog post, or landing page. Model page types by hand in the [Type Builder](https://prismic.io/docs/type-builder.md), or with the [Prismic CLI](https://prismic.io/docs/cli.md) for AI agents. TypeScript types are generated automatically.

[Learn how to create page types](https://prismic.io/docs/content-modeling.md#how-to-create-a-page-type)

## Write page components

Each page type needs a SvelteKit page component. With [file-system-based routing](https://svelte.dev/docs/kit/routing#page), you create a page file at each page's path.

The example below shows a page component for a **Page** page type.

> The `[[preview=preview]]` directory supports [content previews](#preview-draft-content).

```ts filename=src/routes/[[preview=preview]]/[uid]/+page.server.ts collapsed
import type { PageServerLoad, EntryGenerator } from "./$types";
import { createClient } from "$lib/prismicio";

export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
  const client = createClient({ fetch, cookies });
  const page = await client.getByUID("page", params.uid);

  return { page };
};

export const entries: EntryGenerator = async () => {
  const client = createClient();
  const pages = await client.getAllByType("page");

  return pages.map((page) => ({ uid: page.uid }));
};
```

```svelte filename=src/routes/[[preview=preview]]/[uid]/+page.svelte collapsed
<script lang="ts">
  import { isFilled, asImageSrc } from "@prismicio/client";
  import { SliceZone } from "@prismicio/svelte";
  import { components } from "$lib/slices";
  import type { PageProps } from "./$types";

  const { data }: PageProps = $props();
</script>

<svelte:head>
  <title>{data.page.data.meta_title}</title>
  {#if isFilled.keyText(data.page.data.meta_description)}
    <meta name="description" content={data.page.data.meta_description} />
  {/if}
  {#if isFilled.image(data.page.data.meta_image)}
    <meta property="og:image" content={asImageSrc(data.page.data.meta_image)} />
  {/if}
</svelte:head>

<SliceZone slices={data.page.data.slices} {components} />
```

## Define routes

The Prismic CLI keeps [routes](https://prismic.io/docs/routes.md) in `prismic.config.json` in sync with your page types, whether you model in the Type Builder or with CLI commands.

Page routes are inferred from each page type's API ID:

* A page type named **Homepage** maps to `/`.
* A page type named **Page** maps to `/:uid`.
* Any other page type, like **Blog Post**, maps to `/<api-id>/:uid` (e.g. `/blog-post/:uid`).

```json filename=prismic.config.json
{
  "repositoryName": "example-prismic-repo",
  "routes": [
    { "type": "homepage", "path": "/" },
    { "type": "page", "path": "/:uid" },
    { "type": "blog_post", "path": "/blog/:uid" }
  ]
}
```

Edit `prismic.config.json` directly to customize routes. Make sure your routes match your SvelteKit file-system routes. Here are common examples:

| Prismic route                | SvelteKit file-system route                              |
| ---------------------------- | -------------------------------------------------------- |
| `/`                          | `src/routes/[[preview=preview]]/+page.svelte`            |
| `/:uid`                      | `src/routes/[[preview=preview]]/[uid]/+page.svelte`      |
| `/blog/:uid`                 | `src/routes/[[preview=preview]]/blog/[uid]/+page.svelte` |
| `/:grandparent/:parent/:uid` | `src/routes/[[preview=preview]]/[...path]/+page.svelte`  |

[Learn more about routes](https://prismic.io/docs/routes.md)

# Create slices

Content writers build pages from reusable sections called [slices](https://prismic.io/docs/slices.md), like a block of text, a hero, or a call to action. Model slices by hand in the [Type Builder](https://prismic.io/docs/type-builder.md), or with the [Prismic CLI](https://prismic.io/docs/cli.md) for AI agents. TypeScript types are generated automatically.

[Learn how to create slices](https://prismic.io/docs/slices.md#how-to-create-a-slice)

## Write Svelte components

The Prismic CLI generates a starter component in `src/lib/slices/<SliceName>/index.svelte`. Edit the component to add your implementation.

The following example **Call to Action** component displays a rich text field and a link.

```svelte filename=src/lib/slices/CallToAction/index.svelte
<script lang="ts">
  import type { Content } from "@prismicio/client";
  import {
    PrismicRichText,
    PrismicLink,
    type SliceComponentProps,
  } from "@prismicio/svelte";

  type Props = SliceComponentProps<Content.CallToActionSlice>;

  let { slice }: Props = $props();
</script>

<section class="flex flex-col gap-4 p-8">
  <PrismicRichText field={slice.primary.text} />
  <PrismicLink field={slice.primary.link} class="button" />
</section>
```

[Learn how to display content](#display-content)

# Fetch content

Use [`@prismicio/client`](https://prismic.io/docs/technical-reference/prismicio-client.md) and its methods to fetch page content.

## The Prismic client

The Prismic CLI creates a `$lib/prismicio.ts` file when you run `prismic init`. It centralizes your client configuration, including [routes](https://prismic.io/docs/routes.md) read from `prismic.config.json`.

```ts filename=src/lib/prismicio.ts collapsed
import { createClient as baseCreateClient, type Route } from "@prismicio/client";
import {
  enableAutoPreviews,
  type CreateClientConfig,
} from "@prismicio/svelte/kit";
import prismicConfig from "../../prismic.config.json";

export const repositoryName = prismicConfig.repositoryName;

export function createClient({ cookies, ...config }: CreateClientConfig = {}) {
  const client = baseCreateClient(repositoryName, {
    routes: prismicConfig.routes as Route[],
    ...config,
  });

  enableAutoPreviews({ client, cookies });

  return client;
}
```

> **Tip**: `npx prismic gen setup` creates the client file if it doesn't exist.

## Fetch content in pages and slices

Import `createClient()` from `$lib/prismicio.ts` and create a client. Use the client to fetch content. The following example fetches content for a `/[uid]` dynamic route.

```ts filename=src/routes/[[preview=preview]]/[uid]/+page.server.ts {1-2,4-6}
import type { PageServerLoad } from "./$types";
import { createClient } from "$lib/prismicio";

export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
  const client = createClient({ fetch, cookies });
  const page = await client.getByUID("page", params.uid);

  return { page };
};
```

We do not recommend fetching content in slices on the client. Instead, you can use a [content relationship](https://prismic.io/docs/fields/content-relationship.md) field to fetch linked content. You can also use [`<SliceZone>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#slicezone)'s `context` prop to pass arbitrary data from a page to a slice.

[Learn more about fetching content](https://prismic.io/docs/fetch-content.md)

## Secure with an access token

Published content is public by default. You can require a private **access token** to secure the API.

[Learn more about content visibility](https://prismic.io/docs/fetch-content.md#content-visibility)

1. **Generate an access token**

   ```sh
   npx prismic token create
   ```

   Save the new access token as an environment variable in a [`.env`](https://svelte.dev/docs/kit/$env-static-private) file.

   ```sh filename=.env
   PRISMIC_ACCESS_TOKEN=my-access-token
   ```

   Access tokens can also be managed in your repository at **Settings** → **API & Security**.

2. **Pass the access token to your client**

   Provide the token via the `accessToken` option in `$lib/prismicio.ts`:

   ```ts filename=src/lib/prismicio.ts
   import { PRISMIC_ACCESS_TOKEN } from "$env/static/private"; // [!code ++]

   export function createClient({ cookies, ...config }: CreateClientConfig = {}) {
     const client = baseCreateClient(repositoryName, {
       accessToken: PRISMIC_ACCESS_TOKEN, // [!code ++]
       routes: prismicConfig.routes as Route[],
       ...config,
     });

     enableAutoPreviews({ client, cookies });

     return client;
   }
   ```

3. **Change your repository's API access**

   Set the API access to **private**. Content API requests will immediately require an access token.

   ```sh
   npx prismic repo set-api-access private
   ```

   API visibility can also be managed in your repository at **Settings** → **API & Security**.

> **Important**
>
> The access token is a secret. Do not use it in client-side requests.

# Display content

Display content using [`@prismicio/svelte`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md). Here are the most commonly used components:

* [`<PrismicLink>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismiclink) - Display links.
* [`<PrismicImage>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismicimage) - Display images.
* [`<PrismicRichText>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismicrichtext) - Display rich text.
* [`<PrismicText>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#prismictext) - Display plain text.
* [`<SliceZone>`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#slicezone) - Display slices.

[Learn how to display content from a field](https://prismic.io/docs/fields.md)

# Live previews in the Page Builder

The [Page Builder](https://prismic.io/docs/previews.md#live-previews) shows live-updating thumbnails for each [slice](https://prismic.io/docs/slices.md) as content writers edit.

Live previews are powered by the **slice simulator**, a special route that renders individual slices. The Prismic CLI creates the simulator page at `src/routes/slice-simulator/+page.svelte` when you run `prismic init`.

Tell Prismic where your simulator is running so the Page Builder can load it:

```sh
npx prismic preview set-simulator http://localhost:5173
```

The simulator URL can also be set from any document. Click the "**...**" button next to the Publish/Unpublish button in the top-right corner and select **Live preview settings**.

Once your website is deployed, update the simulator URL to your production domain.

> **Tip**: `npx prismic gen setup` creates the simulator route if it doesn't exist.

# Preview draft content

[Full-website previews](https://prismic.io/docs/previews.md#full-website-previews) let content writers see draft content on your website before publishing. The `prismic init` command creates the routes and configuration needed for previews:

* `src/routes/api/preview/+server.ts` — starts a draft preview session using [`redirectToPreviewURL()`](https://prismic.io/docs/technical-reference/prismicio-svelte/v2.md#redirecttopreviewurl).
* `src/params/preview.ts` — a [route matcher](https://svelte.dev/docs/kit/advanced-routing#Matching) that dynamically renders pages prefixed with `/preview` (e.g. `/preview/about`).
* `src/routes/[[preview=preview]]/` — an optional `preview` route segment that wraps your pages.
* `src/routes/+layout.server.ts` — sets [`prerender`](https://svelte.dev/docs/kit/page-options#prerender) to `auto` so pages prefixed with `/preview` skip prerendering and fetch draft content.

Add a development preview URL so you can preview drafts locally:

```sh
npx prismic preview add http://localhost:5173/api/preview --name Development
```

Once deployed, add your production URL as a second preview. Previews can also be managed at **Settings** → **Previews**.

> **Tip**: `npx prismic gen setup` creates the preview routes if they don't exist.

# Deploy

Deploy your SvelteKit website with your hosting provider:

* [Vercel](https://svelte.dev/docs/kit/adapter-vercel)
* [Netlify](https://svelte.dev/docs/kit/adapter-netlify)
* [AWS Amplify](https://docs.aws.amazon.com/amplify/latest/userguide/get-started-sveltekit.html)
* [Cloudflare](https://svelte.dev/docs/kit/adapter-cloudflare)

## Handle content changes

Your website needs to rebuild when content changes in Prismic. Follow the instructions in our [webhooks documentation](https://prismic.io/docs/webhooks.md#add-a-webhook-to-your-hosting-provider) for your hosting provider.

Register the rebuild endpoint as a webhook so Prismic calls it when content is published or unpublished:

```sh
npx prismic webhook create https://example.com/your-rebuild-endpoint \
  --trigger documentsPublished \
  --trigger documentsUnpublished
```

Webhooks can also be managed at **Settings** → **Webhooks**.

# SEO

[Page types](#create-pages) automatically include fields in an **SEO & Metadata** tab:

* `meta_title`: The page's title.
* `meta_description`: The page's description.
* `meta_image`: A preview image when your page is shared on social platforms.

Use the metadata fields in a `+page.svelte`'s [`<svelte:head>`](https://svelte.dev/docs/svelte/svelte-head).

```svelte filename=src/routes/[[preview=preview]]/[uid]/+page.svelte
<svelte:head>
  <title>{data.page.data.meta_title}</title>
  {#if isFilled.keyText(data.page.data.meta_description)}
    <meta name="description" content={data.page.data.meta_description} />
  {/if}
  {#if isFilled.image(data.page.data.meta_image)}
    <meta property="og:image" content={asImageSrc(data.page.data.meta_image)} />
  {/if}
</svelte:head>
```

# Internationalization

Prismic supports multi-lingual websites. See [Locales](https://prismic.io/docs/languages-locales.md) for details on locale management.

1. **Add locales to your repository**

   Use the [Prismic CLI](https://prismic.io/docs/cli.md) to add locales.

   ```sh
   npx prismic locale add fr-fr
   ```

   Locales can also be managed in your repository at **Settings** → **Translations & Locales**.

2. **Install the necessary packages**

   Install the packages needed for locale detection:

   ```sh
   npm install negotiator @formatjs/intl-localematcher
   ```

3. **Create a `$lib/i18n.ts` file**

   The `$lib/i18n.ts` file provides a set of internationalization helpers. The helpers will be used in your website's [`handle`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle) hook.

   Create a `$lib/i18n.ts` file with the following contents.

   ```ts filename=src/lib/i18n.ts
   import { redirect, type RequestEvent } from "@sveltejs/kit";
   import { match } from "@formatjs/intl-localematcher";
   import Negotiator from "negotiator";

   /**
    * A record of locales mapped to a version displayed in URLs. The first entry is
    * used as the default locale.
    */
   // TODO: Update this object with your website's supported locales. Keys
   // should be the locale IDs registered in your Prismic repository, and values
   // should be the string that appears in the URL.
   const LOCALES: Record<string, string> = {
     "en-us": "en-us",
     "fr-fr": "fr-fr",
   };

   /** Redirects with an auto-detected locale prepended to the URL. */
   export function redirectToLocale(event: RequestEvent) {
     const headers = {
       "accept-language":
         event.request.headers.get("accept-language") ?? undefined,
     };
     const languages = new Negotiator({ headers }).languages();
     const locales = Object.keys(LOCALES);
     const locale = match(languages, locales, locales[0]);

     const destination = new URL(event.url);
     destination.pathname = `/${LOCALES[locale]}${event.url.pathname}`;

     redirect(302, destination);
   }

   /** Determines if a pathname has a locale as its first segment. */
   export function pathnameHasLocale(event: RequestEvent): boolean {
     const regexp = new RegExp(`^/(${Object.values(LOCALES).join("|")})(/|$)`);

     return regexp.test(event.url.pathname);
   }

   /**
    * Returns the full locale of a given locale. It returns `undefined` if the
    * locale is not in the master list.
    */
   export function reverseLocaleLookup(locale: string): string | undefined {
     for (const key in LOCALES) {
       if (LOCALES[key] === locale) {
         return key;
       }
     }
   }
   ```

4. **Define locales in `$lib/i18n.ts`**

   `$lib/i18n.ts` contains a list of locales supported by your website.

   Update the `LOCALES` constant to match your Prismic repository's locales. The first locale is used when a visitor's locale is not supported.

   ```ts filename=src/lib/i18n.ts
   const LOCALES = {
     "en-us": "en-us",
     "fr-fr": "fr-fr",
   };
   ```

   You can define how the locale appears in the URL by changing the object's values. In the following example, the `en-us` locale will appear as `en` in the URL (e.g. `/en/about`).

   ```ts filename=src/lib/i18n.ts {2-3}
   const LOCALES = {
     "en-us": "en",
     "fr-fr": "fr",
   };
   ```

   > Statically defining your locales avoids a Prismic API call, optimizing your website's performance.

5. **Create or modify `hooks.server.ts`**

   Website visitors will be directed to the correct locale using [`handle`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle) in `src/hooks.server.ts`.

   Create a `src/hooks.server.ts` file with the following contents, or modify your existing `src/hooks.server.ts` to include the highlighted lines.

   ```ts filename=src/hooks.server.ts
   import { type Handle } from "@sveltejs/kit";
   import { pathnameHasLocale, redirectToLocale } from "$lib/i18n"; // [!code ++]

   // prettier-ignore
   export const handle: Handle = async ({ event, resolve }) => {
     if (!pathnameHasLocale(event)) { // [!code ++]
       redirectToLocale(event); // [!code ++]
     } // [!code ++]

     return resolve(event);
   };
   ```

   [Learn about SvelteKit hooks](https://svelte.dev/docs/kit/hooks)

6. **Nest routes under a `[lang]` dynamic route segment**

   Create a directory at `src/routes/[[preview=preview]]/[lang]` and nest all other routes under this directory.

   The `lang` route parameter will contain the visitor's locale. For example, `/en-us/about` sets `lang` to `"en-us"`.

   [Learn about SvelteKit routing](https://svelte.dev/docs/kit/routing)

7. **Fetch content from the visitor's locale**

   In your `+page.server.ts` files, forward the `lang` parameter to your Prismic query.

   ```ts filename=src/routes/[[preview=preview]]/[lang]/[uid]/+page.server.ts {1,6-8}
   import type { PageServerLoad } from "./$types";
   import { createClient } from "$lib/prismicio";
   import { reverseLocaleLookup } from "$lib/i18n";

   export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
     const client = createClient({ fetch, cookies });
     const page = await client.getByUID("page", params.uid, {
       lang: reverseLocaleLookup(params.lang),
     });

     return { page };
   };
   ```

   The `reverseLocaleLookup` helper from `$lib/i18n.ts` converts a shortened locale (e.g. `en`) to its full version (e.g. `en-us`).

8. **Fetch all locales in `entries`**

   > This step is needed if you statically build pages using [`entries`](https://svelte.dev/docs/kit/page-options#entries), which we recommend.

   Fetch pages from all locales using the Prismic client's `lang: "*"` option. Include the `lang` route parameter.

   ```ts filename=src/routes/[[preview=preview]]/[lang]/[uid]/+page.server.ts {1-2,5,10}
   import type { EntryGenerator } from "./$types";
   import { createClient } from "$lib/prismicio";

   export const entries: EntryGenerator = async () => {
     const client = createClient();
     const pages = await client.getAllByType("page", {
       lang: "*",
     });

     return pages.map((page) => ({ lang: page.lang, uid: page.uid }));
   };
   ```
