---
url: 'https://www.corbado.com/blog/remix-passkeys'
title: 'How to Build a Remix 2 Passkey Login'
description: 'This tutorial shows how to implement passkeys in Remix apps. Using the Corbado Remix component adding passkeys is a matter of a few lines of code.'
lang: 'en'
author: 'Lukas'
date: '2024-03-18T00:00:00.000Z'
lastModified: '2026-04-15T06:00:10.291Z'
keywords: 'remix, Remix'
category: 'Passkeys Implementation'
---

# How to Build a Remix 2 Passkey Login

> ⚠️ **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 Remix component** adds passkey authentication to Remix 2.8.1 apps with minimal
  code, installed via the `@corbado/react` and `@corbado/types` npm packages.
- A **client-only file workaround** is required: naming component files with `.client.tsx`
  prevents Corbado packages from being included in Remix's server-side bundle during
  build.
- The **CorbadoAuth component** manages all signup and login flows including fallback
  solutions. The **PasskeyList component** lets authenticated users view and manage their
  registered passkeys.
- The **Relying Party ID** must be the bare domain only (no protocol, port or path) and is
  configured in Corbado's developer panel under Settings &gt; General &gt; URLs.

## 1. Introduction

In this blog post, well be walking through the process of building a sample application
with passkey authentication using Remix 2. Well cover how to embed the Corbado Remix
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 repository on GitHub](https://github.com/corbado/example-passkeys-remix).

The result looks as follows:

![Remix Passkeys Signup Page](https://www.corbado.com/website-assets/65f8409774eaa39564b48e0a_signup_screen_0723de4815.png)

## 2. Prerequisites

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

## 3. Repository structure

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

```txt
.
├── .env
├── package.json
└── app
    ├── entry.client.tsx
    ├── root.tsx
    ├── utilities
    │   ├── AuthComponent.client.tsx
    │   └── ProfileComponent.client.tsx
    └── routes
        ├── _index.tsx
        └── profile.tsx
```

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

## 4. Set up the Remix Project

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

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

```bash
npx create-remix@latest --template remix-run/remix/templates/vite
```

You will be prompted the following options:

Where should we create your new project?

- If you already have a project folder, just choose "." otherwhise choose any name you
  like

Initialize a new git Repository?

- We recommend to opt for "yes" here

Install dependencies with npm?

- Choose yes

If you created a new project directory, navigate into it with:

```bash
cd <your directory name>
```

If you run

```bash
npm run dev
```

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

![Remix Sample Application](https://www.corbado.com/website-assets/65f844e98912dc3fb1ead02c_remix_skeleton_app_9307bec471.png)

## 5. Set up the Corbado Remix Component for Passkey Authentication

### 5.1 Set up Your Corbado Account and Project

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

![Corbado Developer Panel](https://www.corbado.com/website-assets/65f8462f68752216144afdc6_corbado_dev_panel_511a99977f.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, youll
get more foundational setup guidance. The subsequent sections of this article will delve
into the integration part.

Next, we set the Application URL and [Relying Party](https://www.corbado.com/glossary/relying-party) ID inside
[Settings > General > URLs](https://app.corbado.com/app/settings/general/urls) to the
following values (see explanation below):

![Corbado Developer Panel URL Settings](https://www.corbado.com/website-assets/65f846f857e768b76b0f97c2_url_settings_48aa69a459.png)

- **Application URL:** Provide the URL where your application is running, 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

### 5.2 Embed the Remix component in your Frontend

First, we need to install the [React](https://www.corbado.com/blog/react-passkeys) component with its
corresponding types, so that we have full typescript support:

```bash
npm i @corbado/react @corbado/types
```

First, delete the server entry file under /app/entry.server.tsx as we wont need any custom
logic for the server entrypoint here. If this file isnt present, Remix will automatically
handle the logic for us.

Create a .env file and put your Corbado Project Id found under
[Settings > General > Project info](https://app.corbado.com/app/settings/general) inside.

```txt
VITE_PUBLIC_CORBADO_PROJECT_ID=<Your project ID>
```

Now, head into the entry.client.tsx file and wrap your entire application with the
CorbadoProvider. Hand it your project id from the environment and set darkMode to off.
Theres much more configuration possible which you can find in our
[docs](https://docs.corbado.com/frontend-integration/ui-components/corbadoprovider).

```js
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { CorbadoProvider } from "@corbado/react";

startTransition(() => {
    hydrateRoot(
        document,
        <StrictMode>
            <CorbadoProvider
                projectId={import.meta.env.VITE_PUBLIC_CORBADO_PROJECT_ID}
                darkMode="off"
            >
                <RemixBrowser />
            </CorbadoProvider>
        </StrictMode>,
    );
});
```

For all of the components and hooks were going to use from Corbado well need a workaround
in order to prevent the Corbado [React](https://www.corbado.com/blog/react-passkeys) Package from being inlcuded
inside the server bundle during build. This can be achieved by only using the Corbado
[React](https://www.corbado.com/blog/react-passkeys) package inside files that have .client before their file
extension (e.g. someComponent.client.tsx) and providing a fallback for the components
during ssr. This can be achieved by exporting a clientLoader as well as a HydrateFallback
from your Components (find documentation on this
[here](https://remix.run/docs/en/main/route/hydrate-fallback)). Youll see this in action
in the coming steps.

Next, well need some utilities which we are going to place inside /app/utilities. Start by
creating an AuthComponent.client.tsx file. The
[useCorbado](https://docs.corbado.com/frontend-integration/react/usecorbado) hook provides
us with authentication related information and the
[CorbadoAuth](https://docs.corbado.com/frontend-integration/ui-components/corbadoauth)
component manages all signup and login flows including fallback solutions for us.

```js
import { CorbadoAuth, useCorbado } from "@corbado/react";
import { useNavigate } from "@remix-run/react";

export default function AuthComponent() {
    const { isAuthenticated, loading } = useCorbado();
    const navigate = useNavigate();

    if (isAuthenticated && !loading) {
        navigate("/profile");
    }

    function onLoggedIn() {
        navigate("/profile");
    }

    return <CorbadoAuth onLoggedIn={onLoggedIn} />;
}
```

If the user is already authenticated or once he authenticates, well redirect him to the
/profile route.

Remove the skeleton code from \_index.tsx and display our component inside of the Index
function. For the mentioned workaround we now have to export two more functions:

The hydrateFallback which will tell Remix what fallback we want to use during server side
rendering as well as a clientLoader to enforce rendering on the client. You can read more
about this [here](https://remix.run/docs/en/main/route/hydrate-fallback).

```js
import type { MetaFunction } from "@remix-run/node";
import AuthComponent from "~/utilities/AuthComponent.client";

export const meta: MetaFunction = () => {
  return [
    { title: "New Remix App" },
    { name: "description", content: "Welcome to Remix!" },
  ];
};

// this is needed so ssr of the AuthComponent is prevented
export function clientLoader() {
  return null;
}

// Provide a fallback for ssr as trying to render Corbado Components on the server doesn't work
export function HydrateFallback() {
  return <p>Loading...</p>;
}

export default function Index() {
  return (
    <div>
        <AuthComponent />
    </div>
  );
}
```

### 5.3 Set up the Profile Page

Now, lets create the Profile page. Create another utility called
ProfileComponent.client.tsx. Here, well use
[useCorbado](https://docs.corbado.com/frontend-integration/react/usecorbado) once again to
retrieve authentication state and a logout utility as well as the
[useCorbadoSession](https://docs.corbado.com/frontend-integration/react/usecorbadosession)
hook to retrieve the user object, which lets us access information like his name and
email. If the user isnt authenticated, well redirect him to the homepage where our auth
component is displayed. We also incldue the PasskeyList component which lets the user view
and manage his existing passkeys.

```js
import { PasskeyList, useCorbado, useCorbadoSession } from "@corbado/react";
import { useNavigate } from "@remix-run/react";

export default function Profile() {
    const navigate = useNavigate();
    const { logout, isAuthenticated, loading } = useCorbado();
    const { user } = useCorbadoSession();

    if (!isAuthenticated && !loading) {
        navigate("/");
    }

    return (
        <div style={{ maxWidth: "600px", margin: "0 auto" }}>
            <h1>Profile</h1>
            <p>Name: {user?.name}</p>
            <p>Email: {user?.email}</p>
            <button onClick={logout}>Logout</button>
            <PasskeyList />
        </div>
    );
}
```

Lastly, create the profile route by creating a profile.tsx file under /app/routes and
apply the same workaround as in \_index.tsx.

```js
import ProfileComponent from "~/utilities/ProfileComponent.client";

export function clientLoader() {
    return null;
}

export function HydrateFallback() {
    return <p>Loading...</p>;
}

export default function Profile() {
    return <ProfileComponent />;
}
```

### 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:

![Corbado Passkeys Signup Screen](https://www.corbado.com/website-assets/65f8409774eaa39564b48e0a_signup_screen_65f2f8d676.png)

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

![Corbado Remix Profile Page](https://www.corbado.com/website-assets/65f84b5b5a96255ad285217b_profile_page_3dc659f653.png)

## 6. Conclusion

This tutorial showed how easy it is to add
[passwordless authentication](https://www.corbado.com/glossary/passwordless-authentication) with passkeys to a
Remix 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 read
more about how you can leverage Corbados session management to retrieve backend data,
please check out our documentation [here ](https://docs.corbado.com/sessions/overview)or
if you want to add Corbado to your existing app with existing users, please see our
documentation [here](https://docs.corbado.com/products/corbado-connect).

## Frequently Asked Questions

### How do I stop Corbado React components from breaking Remix server-side rendering?

Name any file containing Corbado components with a `.client.tsx` extension to exclude it
from the server bundle. Additionally, export a `clientLoader` function and a
`HydrateFallback` from each affected route to provide a fallback during SSR and enforce
client-only rendering.

### What hooks does the Corbado React package provide for managing authentication state in a Remix app?

The `useCorbado` hook exposes `isAuthenticated`, `loading` state and a `logout` utility.
The `useCorbadoSession` hook provides access to the user object, including properties like
name and email, for use on protected pages like a profile route.

### How do I configure passkey authentication for local Remix development using Corbado?

In the Corbado developer panel under Settings &gt; General &gt; URLs, set the Application
URL to `http://localhost:5173` and the Relying Party ID to `localhost` with no protocol,
port or path. This correctly binds passkeys to your local development domain.

### What is the minimum code needed to wrap a Remix 2 app with Corbado passkey support?

In `entry.client.tsx`, import `CorbadoProvider` from `@corbado/react` and wrap the
`RemixBrowser` component with it, passing your project ID from an environment variable
(`VITE_PUBLIC_CORBADO_PROJECT_ID`) and a `darkMode` setting. The provider handles session
management and authentication context for the entire app.
