> ## Documentation Index
> Fetch the complete documentation index at: https://www.commercengine.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# SvelteKit

> First-class SvelteKit wrapper with cookie-backed sessions, server load functions, form actions, hooks, and universal load support.

The `@commercengine/storefront/sveltekit` package provides a SvelteKit-specific wrapper around the core Storefront SDK. It gives you three accessors — `publicStorefront()`, `serverStorefront()`, and `clientStorefront()` — with automatic cookie-backed token management across server and client.

### NPM Package

<Card title="@commercengine/storefront" icon="npm" href="https://www.npmjs.com/package/@commercengine/storefront">
  SvelteKit integration for Commerce Engine Storefront SDK. Import from `@commercengine/storefront/sveltekit`.
</Card>

## Installation

<CodeGroup>
  ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark"}}
  npm install @commercengine/storefront
  ```

  ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark"}}
  pnpm add @commercengine/storefront
  ```

  ```bash yarn theme={"theme":{"light":"github-light","dark":"github-dark"}}
  yarn add @commercengine/storefront
  ```
</CodeGroup>

## Quick Start

<Steps>
  <Step title="Set Environment Variables">
    Add your store credentials to `.env`:

    ```bash .env theme={"theme":{"light":"github-light","dark":"github-dark"}}
    PUBLIC_STORE_ID=your-store-id
    PUBLIC_API_KEY=your-api-key
    ```

    <Info>
      SvelteKit uses the `PUBLIC_` prefix for client-exposed env vars (accessed via `$env/static/public`).
    </Info>
  </Step>

  <Step title="Create the Storefront Config">
    A shared config imported by both client and server storefronts:

    ```typescript src/lib/storefront-config.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { Environment } from "@commercengine/storefront";
    import type { SvelteKitStorefrontConfig } from "@commercengine/storefront/sveltekit";
    import { env } from "$env/static/public";

    export const storefrontConfig: SvelteKitStorefrontConfig = {
      storeId: env.PUBLIC_STORE_ID,
      apiKey: env.PUBLIC_API_KEY,
      environment: Environment.Staging, // or Environment.Production
      tokenStorageOptions: { prefix: "myapp_" },
    };
    ```
  </Step>

  <Step title="Create the Client Storefront">
    ```typescript src/lib/storefront.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { createSvelteKitStorefront } from "@commercengine/storefront/sveltekit";
    import { storefrontConfig } from "./storefront-config";

    export const storefront = createSvelteKitStorefront(storefrontConfig);
    ```
  </Step>

  <Step title="Create the Server Storefront">
    Place this under `$lib/server/` — SvelteKit enforces that files in this directory are never imported into client code (a build error is thrown if client code imports from it).

    ```typescript src/lib/server/storefront.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { createSvelteKitServerStorefront } from "@commercengine/storefront/sveltekit/server";
    import { storefrontConfig } from "$lib/storefront-config";

    export const serverStorefront = createSvelteKitServerStorefront(storefrontConfig);
    ```

    <Warning>
      The `@commercengine/storefront/sveltekit/server` import must only be used in server-only files: `+page.server.ts`, `+layout.server.ts`, `hooks.server.ts`, and `+server.ts`. SvelteKit enforces this at build time.
    </Warning>
  </Step>

  <Step title="Bootstrap in Root Layout">
    ```svelte src/routes/+layout.svelte theme={"theme":{"light":"github-light","dark":"github-dark"}}
    <script>
      import { onMount } from "svelte";
      import { storefront } from "$lib/storefront";

      onMount(() => {
        storefront.bootstrap().catch(console.error);
      });
    </script>

    <slot />
    ```

    <Tip>
      `bootstrap()` is deduped and idempotent. If session cookies already exist (returning user), it is a no-op.
    </Tip>
  </Step>
</Steps>

## Accessor Rules

Use the right accessor for each context:

| Context                                              | Accessor                                           | Import From              |
| ---------------------------------------------------- | -------------------------------------------------- | ------------------------ |
| Svelte component — public reads                      | `storefront.publicStorefront()`                    | `$lib/storefront`        |
| Svelte component — session flows                     | `storefront.clientStorefront()`                    | `$lib/storefront`        |
| Client bootstrap                                     | `storefront.bootstrap()`                           | `$lib/storefront`        |
| Universal load (`+page.ts`, `+layout.ts`)            | `storefront.publicStorefront()`                    | `$lib/storefront`        |
| Server load (`+page.server.ts`, `+layout.server.ts`) | `serverStorefront.serverStorefront(cookies)`       | `$lib/server/storefront` |
| Server hooks (`hooks.server.ts`)                     | `serverStorefront.serverStorefront(event.cookies)` | `$lib/server/storefront` |
| Form actions                                         | `serverStorefront.serverStorefront(cookies)`       | `$lib/server/storefront` |
| API routes (`+server.ts`)                            | `serverStorefront.serverStorefront(cookies)`       | `$lib/server/storefront` |

<Info>
  `serverStorefront()` requires passing `cookies` (from `event.cookies` or destructured from the load/action context) because SvelteKit has no global request context. This call is synchronous — no `await` needed. `clientStorefront()` **throws** if called on the server.
</Info>

## Key Patterns

### Public Reads (Universal Load)

For public catalog data that doesn't need a session, use `publicStorefront()` in universal load functions. These run on both server and client:

```typescript src/routes/+page.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { storefront } from "$lib/storefront";

export async function load() {
  const sdk = storefront.publicStorefront();
  const { data, error } = await sdk.catalog.listProducts({ page: 1, limit: 20 });

  if (error) return { products: [], error: error.message };
  return { products: data?.products ?? [] };
}
```

### Session-Aware Server Load

```typescript src/routes/account/+page.server.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { serverStorefront } from "$lib/server/storefront";
import { redirect } from "@sveltejs/kit";

export async function load({ cookies }) {
  const sdk = serverStorefront.serverStorefront(cookies);
  const { data, error } = await sdk.customer.getCustomer();

  if (error) redirect(302, "/login");
  return { customer: data?.customer };
}
```

### Layout Server Load

```typescript src/routes/+layout.server.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { serverStorefront } from "$lib/server/storefront";

export async function load({ cookies }) {
  const sdk = serverStorefront.serverStorefront(cookies);
  const userId = await sdk.session.peekUserId();

  return { isLoggedIn: !!userId };
}
```

### Form Actions (Mutations)

SvelteKit form actions can read and write cookies, making them ideal for cart and auth mutations:

```typescript src/routes/cart/+page.server.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { serverStorefront } from "$lib/server/storefront";
import { fail } from "@sveltejs/kit";

export const actions = {
  addToCart: async ({ cookies, request }) => {
    const formData = await request.formData();
    const productId = formData.get("productId") as string;
    const variantId = formData.get("variantId") as string | null;
    const cartId = formData.get("cartId") as string;

    const sdk = serverStorefront.serverStorefront(cookies);
    const { data, error } = await sdk.cart.addDeleteCartItem(
      { id: cartId },
      { product_id: productId, variant_id: variantId, quantity: 1 }
    );

    if (error) return fail(400, { error: error.message });
    return { cart: data?.cart };
  },

  addToWishlist: async ({ cookies, request }) => {
    const formData = await request.formData();
    const productId = formData.get("productId") as string;

    const sdk = serverStorefront.serverStorefront(cookies);
    const { data, error } = await sdk.cart.addToWishlist({
      product_id: productId,
      variant_id: null,
    });

    if (error) return fail(400, { error: error.message });
    return { wishlist: data };
  },
};
```

### API Routes

```typescript src/routes/api/wishlist/+server.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { serverStorefront } from "$lib/server/storefront";
import { json, error } from "@sveltejs/kit";

export async function GET({ cookies }) {
  const sdk = serverStorefront.serverStorefront(cookies);
  const { data, error: apiError } = await sdk.cart.getWishlist();

  if (apiError) error(500, apiError.message);
  return json(data);
}

export async function POST({ cookies, request }) {
  const body = await request.json();
  const sdk = serverStorefront.serverStorefront(cookies);
  const { data, error: apiError } = await sdk.cart.addToWishlist({
    product_id: body.productId,
    variant_id: body.variantId ?? null,
  });

  if (apiError) error(500, apiError.message);
  return json(data);
}
```

### Server Hooks

Use hooks for auth guards and injecting session data into `event.locals`:

```typescript src/hooks.server.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { serverStorefront } from "$lib/server/storefront";
import type { Handle } from "@sveltejs/kit";

export const handle: Handle = async ({ event, resolve }) => {
  const sdk = serverStorefront.serverStorefront(event.cookies);
  const userId = await sdk.session.peekUserId();

  event.locals.userId = userId;

  if (event.url.pathname.startsWith("/account") && !userId) {
    return new Response(null, { status: 302, headers: { Location: "/login" } });
  }

  return resolve(event);
};
```

To make `event.locals.userId` type-safe, declare it in `app.d.ts`:

```typescript src/app.d.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
declare global {
  namespace App {
    interface Locals {
      userId: string | null;
    }
  }
}

export {};
```

### Client-Side Fetching (After Hydration)

In Svelte components, use the client accessors directly:

```svelte theme={"theme":{"light":"github-light","dark":"github-dark"}}
<script>
  import { onMount } from "svelte";
  import { storefront } from "$lib/storefront";

  let products = [];

  onMount(async () => {
    const sdk = storefront.publicStorefront();
    const { data } = await sdk.catalog.listProducts({ limit: 20 });
    products = data?.products ?? [];
  });
</script>

{#each products as product}
  <div>{product.name}</div>
{/each}
```

Session-bound operations (cart, wishlist, account):

```svelte theme={"theme":{"light":"github-light","dark":"github-dark"}}
<script>
  import { storefront } from "$lib/storefront";

  async function addToWishlist(productId: string) {
    const sdk = storefront.clientStorefront();
    await sdk.cart.addToWishlist({
      product_id: productId,
      variant_id: null,
    });
  }
</script>
```

### Static Prerendering

For prerendered pages, use `publicStorefront()` in a universal load function:

```typescript src/routes/products/[slug]/+page.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { storefront } from "$lib/storefront";

export const prerender = true;

export async function load({ params }) {
  const sdk = storefront.publicStorefront();
  const { data, error } = await sdk.catalog.getProductDetail({
    product_id_or_slug: params.slug,
  });

  if (error || !data) return { product: null };
  return { product: data.product };
}
```

<Info>
  `publicStorefront()` never creates sessions, never reads/writes cookies, and is safe for prerender and build-time contexts.
</Info>

## Hosted Checkout + SvelteKit

If you use [Commerce Engine Hosted Checkout](/hosted-checkout/svelte), follow this pattern to keep the storefront SDK session and checkout session in sync.

<Steps>
  <Step title="Add onTokensUpdated to Storefront Config">
    This callback forwards token updates from the storefront SDK to the checkout iframe.

    ```typescript src/lib/storefront-config.ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { Environment } from "@commercengine/storefront";
    import type { SvelteKitStorefrontConfig } from "@commercengine/storefront/sveltekit";
    import { env } from "$env/static/public";

    export const storefrontConfig: SvelteKitStorefrontConfig = {
      storeId: env.PUBLIC_STORE_ID,
      apiKey: env.PUBLIC_API_KEY,
      environment: Environment.Staging,
      tokenStorageOptions: { prefix: "myapp_" },
      onTokensUpdated: (accessToken, refreshToken) => {
        if (typeof window !== "undefined") {
          void import("@commercengine/checkout").then(({ getCheckout }) => {
            getCheckout().updateTokens(accessToken, refreshToken);
          });
        }
      },
    };
    ```
  </Step>

  <Step title="Bootstrap + Init Checkout in Root Layout">
    Call both `storefront.bootstrap()` and `initCheckout()` in the root layout. The checkout is initialized with `authMode: "provided"` so the storefront SDK owns the session.

    ```svelte src/routes/+layout.svelte theme={"theme":{"light":"github-light","dark":"github-dark"}}
    <script>
      import { onMount, onDestroy } from "svelte";
      import { storefront } from "$lib/storefront";

      onMount(async () => {
        await storefront.bootstrap();

        const sdk = storefront.clientStorefront();
        const accessToken = await sdk.getAccessToken();
        const refreshToken = await sdk.session.peekRefreshToken();

        const { initCheckout } = await import("@commercengine/checkout");
        initCheckout({
          storeId: import.meta.env.PUBLIC_STORE_ID,
          apiKey: import.meta.env.PUBLIC_API_KEY,
          authMode: "provided",
          accessToken: accessToken ?? undefined,
          refreshToken: refreshToken ?? undefined,
          onTokensUpdated: ({ accessToken, refreshToken }) => {
            void sdk.setTokens(accessToken, refreshToken);
          },
        });
      });

      onDestroy(() => {
        if (typeof window !== "undefined") {
          import("@commercengine/checkout").then(({ destroyCheckout }) => {
            destroyCheckout();
          });
        }
      });
    </script>

    <slot />
    ```

    <Warning>
      Install `@commercengine/checkout` separately if you use hosted checkout. The `authMode: "provided"` setting tells checkout to use your storefront SDK tokens instead of managing its own.
    </Warning>
  </Step>

  <Step title="Use Checkout in Components">
    Then use the `checkout` store in any Svelte component — no provider needed:

    ```svelte src/components/CartButton.svelte theme={"theme":{"light":"github-light","dark":"github-dark"}}
    <script>
      import { checkout } from "@commercengine/checkout/svelte";
    </script>

    <button onclick={() => $checkout.openCart()} disabled={!$checkout.isReady}>
      Cart ({$checkout.cartCount})
    </button>
    ```
  </Step>
</Steps>

## Common Pitfalls

| Level    | Issue                                                       | Solution                                                                                                          |
| -------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| CRITICAL | Using `publicStorefront()` for cart, auth, or order flows   | Use `serverStorefront(cookies)` on the server or `clientStorefront()` in the browser                              |
| CRITICAL | Importing `$lib/server/storefront` in a `.svelte` component | Server storefront must only be used in `+page.server.ts`, `+layout.server.ts`, `hooks.server.ts`, or `+server.ts` |
| HIGH     | Missing bootstrap in root layout                            | Add `onMount` bootstrap in `+layout.svelte`                                                                       |
| HIGH     | Forgetting to pass `cookies` to `serverStorefront()`        | Always pass `cookies` from the load/action/hook context — there is no global cookie access                        |
| HIGH     | Using `@commercengine/ssr-utils` directly                   | Use the first-party wrapper `@commercengine/storefront/sveltekit` instead                                         |
| MEDIUM   | Using server load when universal load suffices              | Use `+page.ts` (universal) for public catalog reads — they work on both server and client                         |

## Best Practices

<CardGroup cols={2}>
  <Card title="Shared Config" icon="gear">
    Export `storefrontConfig` from `storefront-config.ts` and import it in both client and server storefront modules. Keep configuration in one place.
  </Card>

  <Card title="Server Boundaries" icon="shield-halved">
    Place server storefront in `$lib/server/` — SvelteKit enforces this boundary at build time with a compile error if client code imports from it.
  </Card>

  <Card title="Universal vs Server Load" icon="arrows-split-up-and-left">
    Use universal `+page.ts` with `publicStorefront()` for public catalog reads. Use `+page.server.ts` with `serverStorefront(cookies)` only when you need session data.
  </Card>

  <Card title="Form Actions for Mutations" icon="bolt">
    Auth, cart updates, and order creation should go through form actions using `serverStorefront(cookies)` so cookies are read and written correctly.
  </Card>
</CardGroup>

## Cross-References

<CardGroup cols={2}>
  <Card title="SDK Installation" icon="download" href="/sdk/installation">
    Core SDK setup and framework detection guide.
  </Card>

  <Card title="Token Management" icon="key" href="/sdk/token-management">
    Full token lifecycle, cookie hydration, and returning-user flow.
  </Card>

  <Card title="Authentication Guide" icon="user-shield" href="/storefront/authentication">
    OTP, email, and social login patterns for storefronts.
  </Card>

  <Card title="Hosted Checkout (Svelte)" icon="cart-shopping" href="/hosted-checkout/svelte">
    Svelte integration for Commerce Engine Checkout.
  </Card>
</CardGroup>
