---
url: 'https://www.corbado.com/blog/svelte-passkeys'
title: 'Building a Svelte Passkey Login Page'
description: 'This tutorial shows developers how to integrate passkeys into a Svelte app. Besides passkey authentication, simple session management is also implemented.'
lang: 'en'
author: 'Vincent Delitz'
date: '2023-09-28T00:00:00.000Z'
lastModified: '2026-08-28T15:31:58.487Z'
keywords: 'Svelte'
category: 'Passkeys Implementation'
---

# Building a Svelte Passkey Login Page

> ⚠️ **Archived tutorial - outdated integration**
>
> This article uses Corbado Complete, a discontinued product. Corbado now offers
> **[passkey observability and authentication for enterprise CIAM](https://www.corbado.com/features)**
>
> _The tutorial below is preserved for reference only and is no longer maintained._

## Key Facts

- **Corbado web-js** integrates passkeys into Svelte apps via a mountable UI component,
  requiring only a project ID and an `onLoggedIn` callback to handle successful
  authentication.
- **SSR must be disabled** when using the Corbado web-js package in Svelte. Export
  `ssr = false` from a `+layout.server.ts` file to prevent rendering issues.
- The **Relying Party ID** must be the bare domain only, no protocol, port or path. For
  local development, set it to `localhost` in the Corbado developer panel.
- **Corbado session management** is bundled with the integration, providing access to user
  ID and name via `Corbado.user` without additional backend configuration.

## 1. Introduction

In this blog post, we'll be walking through the process of building a sample application
with passkey authentication using Svelte. We'll cover how to embed the Corbado UI
component and implement [passkey login](https://www.corbado.com/blog/passkey-login-best-practices) functionality
for a seamless user experience.

If you want to see the finished code, please have a look at our
[sample application GitHub repository](https://github.com/corbado/example-passkeys-svelte):

The result looks as follows:

![Svelte Passkeys Login Page](https://www.corbado.com/website-assets/Screenshot_2024_04_25_at_18_18_35_2cb2238934.png)

## 2. Prerequisites

This tutorial assumes basic familiarity with Svelte, HTML, CSS and JavaScript. Lets dive
in! Moreover, you need to have Node and NPM installed on your machine.

## 3. Svelte Passkey Repository Structure

Lets first discuss the structure of our project
([full GitHub repo](https://github.com/corbado/example-passkeys-svelte)):

```
.
├── .env
├── package.json
└── src
    ├── app.html
    └── routes
        ├── +layout.svelte
        ├── +layout.server.ts
        ├── +page.svelte
        └── profile
            └── +page.svelte
```

The rest of the files of this project can be ignored for the purpose of this tutorial.

## 4. Set Up the Svelte Project

In the following, we explain step-by-step what needs to be done to successfully set up the
Svelte project.

Lets start out by initializing a new Svelte project. In this tutorial, were using Svelte
version 4.2.7:

```bash
npm create svelte@latest example-passkeys-svelte
```

In the installation guide steps, we select the following (if youre asked to install or
update the create-svelte package, click Yes to proceed):

- App template: Skeleton project
- Type checking with TypeScript: We're using TypeScript here, but feel free to choose
  whatever you prefer
- Additional options: We're using ESLint and Prettier

If you're asked to proceed click "Yes"

```bash
cd example-passkeys-svelte
```

Let's also install the Corbado web-js dependency:

```bash
npm i @corbado/web-js
```

Optionally, you can also install the Corbado web-js types for an enhanced typescript
experience:

```bash
npm i -D @corbado/types
```

If you run

```bash
npm install
```

and

```bash
npm run dev
```

the sample Svelte skeleton application starts at
[http://localhost:5173](http://localhost:5173):

![Svelte Passkeys Skeleton](https://www.corbado.com/website-assets/65256646fbb47fb7bbe7ff40_svelte_skeleton_0ccafbb6c4.png)

## 5. Set Up the Corbado UI Component for Passkey Authentication

### 5.1 Set up your Corbado account and project

Visit the
[Corbado developer panel](https://app.corbado.com/signin?framework=Svelte&technology=passkeys#register)
to sign up and create your account (you'll see the passkey sign-up in action here!).

![Svelte Corbado Developer Panel Sign-up](https://www.corbado.com/website-assets/dev_panel_d7f6cc96ed.png)

In the project setup wizard, begin by selecting an appropriate name for your project. For
the product selection, opt for "Corbado Complete". Subsequently, specify your technology
stack and select "DEV along with "Corbado session management" options. Afterwards, you'll
get more foundational setup guidance.

Next, choose "Web app" as an application type and Svelte as your framework. In the
application settings, define your application url and
[relying party](https://www.corbado.com/glossary/relying-party) id as follows:

![App settings](https://www.corbado.com/website-assets/application_settings_e7049b45fa.png)

- **Application URL:** Provide the URL where you embedded the UI component, here:
  [http://localhost:5173](http://localhost:5173).
- [**Relying Party ID**](https://www.corbado.com/blog/webauthn-relying-party-id-rpid-passkeys)**:** Provide the
  domain (no protocol, no port and no path) where passkeys should be bound to, here:
  localhost

Lastly, retrieve your project ID from the developer panel and store it in your environment
file. You can find it [here](https://app.corbado.com/app/settings/general/project-info)
under Corbado API access.

Your environment file should look like this:

```txt
VITE_CORBADO_PROJECT_ID=<your-project-id>
```

You'll need it later to embed the Corbado UI component in your Svelte app.

### 5.2 Embed the UI Component in the Frontend

Now, lets jump back to the code we created from [step 4](#set-up-svelte-project).

First, we'll disable ssr as that's currently not supported by the Corbado web-js package,
but we're working on it ;)

We'll do that by creating a +layout.server.ts file with the following content:

```typescript
export const ssr = false;
```

Then, we'll create our outer layout which will handle the initialization of our Corbado
project. It acts as a boundary that only renders the rest of the app once the Corbado
project is initialized.

```svelte
<script lang="ts">
    import Corbado from "@corbado/web-js";
    import {onMount} from "svelte";
    const PROJECT_ID = import.meta.env.VITE_CORBADO_PROJECT_ID;

    let isInitialized = false;
    onMount(async () => {
        await Corbado.load({
            projectId: PROJECT_ID,
            darkMode: 'off'
        });
        isInitialized = true; // Set to true once Corbado is initialized
    });
</script>

<div>
    {#if isInitialized}
        <slot></slot>
    {/if}
</div>
```

Next, we modify the home page. The home page is the file in
[src](https://www.corbado.com/glossary/src)/routes/+page.svelte. This file contains the sign up / login UI
component and navigates to `/profile` once the user is authenticated.

```svelte
<script lang="ts">
    import Corbado from '@corbado/web-js';
    import {onMount} from 'svelte';

    let authElement: HTMLDivElement;

    onMount(() => {
        Corbado.mountAuthUI(
            authElement, {
                onLoggedIn: () => window.location.href = "/profile",
            })
    })
</script>

<div bind:this={authElement}></div>
```

### 5.3 Set Up the Profile Page

Lastly, let's create the profile page under `/profile`. This page will show the user's
name and [user ID](https://www.corbado.com/blog/webauthn-user-id-userhandle) and provide a button to log out. If
the user isn't logged in, we'll display a link back to the homepage.

```svelte
<script lang="ts">
    import Corbado from "@corbado/web-js";
    import {onMount} from "svelte";

    let user: SessionUser | undefined;

    onMount(() => {
        user = Corbado.user
    })

    async function handleLogout() {
        await Corbado.logout()
        window.location.href = "/"
    }
</script>

<div>
    {#if (user)}
        <h1>
            Profile Page
        </h1>
        <p>
            User-id: {user.sub}
        </p>
        <p>
            Name: {user.name}
        </p>
        <button on:click={handleLogout}>
            Logout
        </button>
    {:else}
        <h1>
            You aren't logged in.
        </h1>
        <p>Go <a href="/">Home</a></p>
    {/if}
</div>
```

This page has content that should only be visible to authenticated users. Therefore, we
use Corbado session management in the code above.

### 5.4 Start Using Passkeys

If everything is set up and installed, run the application with

```bash
npm run dev
```

You should see the following screen:

![Svelte Passkeys Login Page](https://www.corbado.com/website-assets/Screenshot_2024_04_25_at_18_38_50_c2cafffc15.png)

After successful sign up / login, you see the profile page:

![Svelte Passkeys Profile Page](https://www.corbado.com/website-assets/Svelte_Corbado_Profile_Page_8d2d4ebb3e.png)

## 6. How Corbado can help

A tutorial gives you one happy path on one machine. Production gives you a message saying
"the login just doesn't work for me", with no error, no screenshot and no way to reproduce
it. [Corbado Observe](https://www.corbado.com/observe) is the
[authentication observability](https://www.corbado.com/blog/authentication-observability) layer, and its User
Search view exists for that message: it replays what one person's login actually did, with
the device context that made it fail attached to each attempt.

[Video: User search debugging](https://www.corbado.com/videos/features/user-search-debugging.mp4) ([See User Debugging in Corbado Observe](https://www.corbado.com/observe/user-debugging))

- **You search with whatever the ticket contains.** A flow ID, the external ID from your
  own user table, a [YubiKey](https://www.corbado.com/glossary/yubikey) serial number or Observe's own
  [user ID](https://www.corbado.com/blog/webauthn-user-id-userhandle) all work, up to a thousand pasted at once,
  although one search cannot mix two kinds of identifier.
- **Every subflow gets its own card.** The identifier step,
  [passkey login](https://www.corbado.com/blog/passkey-login-best-practices), password login,
  [social login](https://www.corbado.com/glossary/social-login) and the OTP channels each render separately with
  their own fields, so the step the person actually reached is visible instead of
  inferred.
- **The environment sits next to the flow.** `Corbado.user` in section 5.3 gives you `sub`
  and `name`, which is everything your Svelte app knows. The flow card gives you what the
  browser reported at the moment of the ceremony, and that is usually the part that
  explains why the same login worked on your laptop.

Worth knowing given section 5.2: because SSR is off, the whole ceremony happens in the
browser, so the browser is also the only place the evidence exists. Nothing in your
[SvelteKit](https://www.corbado.com/blog/sveltekit-passkeys) server logs will contain it.

## 7. Conclusion

This tutorial showed how easy it is to add
[passwordless authentication](https://www.corbado.com/glossary/passwordless-authentication) with passkeys to a
Svelte app using Corbado. Besides the passkey-first authentication, Corbado provides
simple session management, that we used for a retrieval of basic user data. If you want to
add Corbado to your existing app with existing users, see our
[Corbado Connect documentation](https://docs.corbado.com/corbado-connect/overview/welcome).

If you want to learn how you can retrieve user authentication state as well as wells as
user information on the server side, take a look at our
[Sveltekit](https://www.corbado.com/blog/sveltekit-passkeys) blogpost.

## Frequently Asked Questions

### How do I add passkey authentication to a Svelte app?

Install the `@corbado/web-js` package and call `Corbado.mountAuthUI()` inside an `onMount`
hook, passing a container element and an `onLoggedIn` callback. You also need a Corbado
project ID stored in your environment file as `VITE_CORBADO_PROJECT_ID`, which you
retrieve from the Corbado developer panel.

### Why does Corbado require disabling SSR in SvelteKit?

The Corbado web-js package does not currently support server-side rendering, so the UI
component must be initialized on the client only. You disable this by creating a
`+layout.server.ts` file and exporting `ssr = false`, which tells SvelteKit to skip server
rendering for the affected routes.

### What application URL and Relying Party ID should I use when testing Corbado passkeys locally in Svelte?

Set the Application URL to `http://localhost:5173` (the default SvelteKit dev port) and
the Relying Party ID to `localhost` with no protocol, port or path. These values are
configured in the application settings section of the Corbado developer panel.

### How do I retrieve user information and handle logout after a passkey login in Svelte?

After a successful login, access the authenticated user via `Corbado.user`, which exposes
`user.sub` (user ID) and `user.name`. To log out, call `await Corbado.logout()` and then
redirect the user back to the home page.
