zitadel-sdk-sveltekit · git:20261021.ab19e1e · 2026-10-21 · sha256 0f147cdb65c3f3ed
zitadel-sdk-sveltekit git:20261021.ab19e1eC
Immutable. This exact content never changes and is served at /api/v1/blob/0f147cdb65c3f3ed.
---
name: zitadel-sdk-sveltekit
description: >-
Integrate Zitadel authentication into an existing or custom SvelteKit app with
@zitadel/sdk-sveltekit. Use when the user wants login, logout, session
handling, or protected routes in a SvelteKit 2 or 3 app and is wiring the SDK
by hand rather than letting the CLI scaffold a fresh project — adding the
`handle` hook, reading auth state in a load function, or rendering the
`ZitadelLogin` / `ZitadelSession` widgets.
---
# @zitadel/sdk-sveltekit
A SvelteKit `handle` hook and helpers that add Zitadel (Nextgen) auth to a
SvelteKit app. The hook proxies auth traffic same-origin under `/__nextgen`,
verifies the session JWT via JWKS on the server, writes the result to
`event.locals.nextgenAuth`, and redirects unauthenticated requests on protected
routes. The package is server-only; the widgets come from `@zitadel/sdk-svelte`.
## When to use
The `zitadel` CLI (`zitadel setup --framework sveltekit`) scaffolds the hook,
login/register/profile routes, and `.env.local` into a fresh or detected
SvelteKit project — prefer it for greenfield work. Reach for **this skill** when
you are adding auth to an **existing or custom** SvelteKit app by hand, or
adjusting a scaffolded wiring.
## Install
```bash
pnpm add @zitadel/sdk-sveltekit@latest
# the login/logout/session widgets are a separate package:
pnpm add @zitadel/sdk-svelte@latest
```
Peer deps: `@sveltejs/kit ^2.0.0 || ^3.0.0`, `svelte ^5.0.0`.
## Integrate
**1. Server hook.** Create or extend `src/hooks.server.ts`:
```ts
import { createNextgenHandle } from '@zitadel/sdk-sveltekit/server';
export const handle = createNextgenHandle({
url: process.env.ZITADEL_URL,
projectSecret: process.env.ZITADEL_PROJECT_SECRET,
protectedRoutes: ['/admin', '/dashboard*'], // trailing * matches sub-paths
loginPath: '/login',
});
```
Read config from `process.env`, not SvelteKit's `$env` modules — their import
paths moved between SvelteKit 2 and 3. SvelteKit does not surface `.env` files
on `process.env`, so in dev load `.env.local` first with
`process.loadEnvFile?.('.env.local')`. Compose with other hooks via
`sequence()` from `@sveltejs/kit/hooks`. Sessions count only in the app's own
project: `projectId`, else `ZITADEL_PROJECT_ID`, else
`VITE_ZITADEL_PROJECT_ID`. With none set, nobody is signed in.
**2. Read auth server-side** with `getAuth(event)` in a load function or
endpoint, and forward only client-safe fields to the page — never the raw token:
```ts
import { redirect } from '@sveltejs/kit';
import { getAuth } from '@zitadel/sdk-sveltekit/server';
export const load = (event) => {
const auth = getAuth(event);
if (!auth.isAuthenticated) throw redirect(302, '/login');
return { identifier: auth.session.identifier };
};
```
`protectedRoutes` only guards full page loads. To keep a page from rendering at
all without a session, guard it in a `+page.server.ts` load like the one above —
SvelteKit runs that load on the server for client-side navigation too. A
universal `+page.ts` load does not work: it runs in the browser, where `locals`
are not available.
**3. Render the widgets** from `@zitadel/sdk-svelte`, mounted on the client only
(set a flag in `onMount` and wrap the widget in `{#if mounted}`):
```svelte
<script lang="ts">
import { onMount } from 'svelte';
import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-svelte';
const project = configureZitadel({
projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
proxyPath: '/__nextgen',
});
let mounted = $state(false);
onMount(() => (mounted = true));
</script>
{#if mounted}
<ZitadelLogin {project} purpose="login" postSignInUrl="/admin" />
{/if}
```
`<ZitadelSession {project} postSignOutUrl="/login" />` renders the signed-in
card with sign-out, and `<ZitadelLogout>` a sign-out control on its own; mount
them the same way.
## Widget props and events
This SDK is server-only. The widgets come from `@zitadel/sdk-svelte`, so read its skill for the widget props and events.
## Read these, don't guess
The package README at `node_modules/@zitadel/sdk-sveltekit/README.md` and the
bundled `.d.ts` types (`dist/server.d.ts`, `dist/client.d.ts`) are authoritative
and versioned with the installed package — read them before wiring anything, and
let them override anything remembered. This skill teaches the pattern, not a
full catalog of options or the shared JWT-verification pipeline (that lives in
`@zitadel/sdk-core`). For widget props and callbacks, read
`node_modules/@zitadel/sdk-svelte/README.md` and its types. Check exact option
names and defaults against the installed version rather than assuming.
## Gotchas
- **`@zitadel/sdk-sveltekit` renders no widgets.** Importing `ZitadelLogin` from
it fails; it comes from `@zitadel/sdk-svelte`.
- **Mount the widgets client-side only.** Their config is set as element
properties, which server-rendered markup cannot carry; use `onMount`, not the
deprecated `$app/environment` (SvelteKit 3).
- **Keep `App.Locals` declared.** The package merges `nextgenAuth` into it, so
`src/app.d.ts` must keep the `App` namespace with a `Locals` interface.
- **Client-side navigation skips the hook.** Without a `+page.server.ts` guard,
only the `ZitadelSession` card sends a signed-out visitor away.