---
title: Clerk
---

import { StepByStep, Step, StepText, StepCode } from "@site/src/components/Steps";
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

This guide will walk you through integrating **Clerk** authentication with your **SpacetimeDB** React application. You will configure a Clerk application, obtain a JWT from Clerk, and pass it to your SpacetimeDB connection as the authentication token.

## Prerequisites

We assume you have the following prerequisites in place:

- A working SpacetimeDB project. Follow our [React Quickstart Guide](../../00100-intro/00200-quickstarts/00100-react.md) if you need help setting this up.
- A Clerk account.

## Getting started

<StepByStep>

<Step title="Install Clerk React SDK">
<StepText>
Install the Clerk React SDK into your React application.
</StepText>
<StepCode>
<Tabs groupId="package-manager" defaultValue="NPM">
<TabItem value="NPM" label="NPM">
```bash
npm add @clerk/clerk-react
```
</TabItem>
<TabItem value="Yarn" label="Yarn">
```bash
yarn add @clerk/clerk-react
```
</TabItem>
<TabItem value="PNPM" label="PNPM">
```bash
pnpm add @clerk/clerk-react
```
</TabItem>
<TabItem value="Bun" label="Bun">
```bash
bun add @clerk/clerk-react
```
</TabItem>
</Tabs>
</StepCode>
</Step>

<Step title="Setup your Clerk application">
<StepText>

1. Head to the Clerk Dashboard and create (or select) an application.
2. In your app settings, locate your **Publishable key** and save it somewhere handy (you'll need it in `main.tsx`).
3. Ensure your local development URL is allowed in Clerk (for example `http://localhost:5173` if you are using Vite).
4. You will also need a **JWT** issued by Clerk to send to SpacetimeDB. Clerk can mint JWTs via session tokens; in the next steps we will retrieve a token from the active session in the browser.

</StepText>
</Step>

<Step title="Create a ClerkTokenProvider component">
<StepText>
Create a component that:

- Ensures the user is signed in (and redirects them to Clerk's sign-in UI when they are not).
- Retrieves a session token (JWT) from Clerk once authenticated.
- Exposes that token to your app via React context, similar to the `AutoLogin` pattern in the Auth0 tutorial.

Create a file named `ClerkTokenProvider.tsx`.
</StepText>
<StepCode>

```tsx
import React, {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react';
import { useAuth, RedirectToSignIn } from '@clerk/clerk-react';

const TokenContext = createContext<string | undefined>(undefined);

export function useClerkToken() {
  const token = useContext(TokenContext);
  if (!token) {
    throw new Error('useClerkToken must be used within a ClerkTokenProvider');
  }
  return token;
}

/**
 * ClerkTokenProvider:
 * - If signed out: renders Clerk's redirect component.
 * - If signed in: loads a Clerk session token (JWT) and provides it via context.
 *
 * Note:
 * - getToken() returns a token suitable for sending to your backend. If you have
 *   configured a specific JWT template in Clerk, pass its name via
 *   getToken({ template: "<YOUR_TEMPLATE_NAME>" }).
 */
export function ClerkTokenProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const { isLoaded, isSignedIn, getToken } = useAuth();

  const [token, setToken] = useState<string | null>(null);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function run() {
      if (!isLoaded) return;

      // IMPORTANT: if signed out, clear any cached token
      if (!isSignedIn) {
        if (!cancelled) setToken(null);
        return;
      }

      try {
        // If you use a Clerk JWT template, use:
        // const t = await getToken({ template: "<YOUR_TEMPLATE_NAME>" });
        const t = await getToken();

        if (!t) {
          throw new Error('Clerk returned no session token.');
        }

        if (!cancelled) setToken(t);
      } catch (e) {
        if (!cancelled) setError(e as Error);
      }
    }

    run();
    return () => {
      cancelled = true;
    };
  }, [isLoaded, isSignedIn, getToken]);

  const value = useMemo<string | undefined>(() => token ?? undefined, [token]);

  if (error) {
    return (
      <div>
        <p>Authentication error</p>
        <pre>{error.message}</pre>
      </div>
    );
  }

  if (!isLoaded) {
    return <p>Loading...</p>;
  }

  if (!isSignedIn) {
    // Sends the user to Clerk sign-in. After sign-in, they return to the app.
    return <RedirectToSignIn />;
  }

  if (!token) {
    return <p>Loading...</p>;
  }

  return (
    <TokenContext.Provider value={value}>{children}</TokenContext.Provider>
  );
}
```

</StepCode>
</Step>

<Step title="Wrap your application with ClerkProvider in main.tsx">
<StepText>
Wrap your app with `ClerkProvider` so Clerk can manage authentication state.

</StepText>
<StepCode>

```tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { ClerkProvider } from '@clerk/clerk-react';

import App from './App.tsx';
import { ClerkTokenProvider } from './ClerkTokenProvider.tsx';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ClerkProvider publishableKey="<YOUR_CLERK_PUBLISHABLE_KEY>">
      <ClerkTokenProvider>
        <App />
      </ClerkTokenProvider>
    </ClerkProvider>
  </StrictMode>
);
```

</StepCode>
</Step>

<Step title="Update App.tsx to use the Clerk token">
<StepText>
Update your `App.tsx` file to:

1. Read the Clerk token via `useClerkToken`.
2. Pass it to the `DbConnection` builder using `.withToken(...)`.

This mirrors the Auth0 flow: SpacetimeDB receives a bearer token (JWT) and can validate it server-side.
</StepText>
<StepCode>

```tsx
import { useMemo } from 'react';
import { Identity } from 'spacetimedb';
import { SpacetimeDBProvider } from 'spacetimedb/react';
import { DbConnection, ErrorContext } from './module_bindings';
import { useClerkToken } from './ClerkTokenProvider';

const onConnect = (_conn: DbConnection, identity: Identity) => {
  console.log(
    'Connected to SpacetimeDB with identity:',
    identity.toHexString()
  );
};

const onDisconnect = () => {
  console.log('Disconnected from SpacetimeDB');
};

const onConnectError = (_ctx: ErrorContext, err: Error) => {
  console.log('Error connecting to SpacetimeDB:', err);
};

export default function App() {
  const token = useClerkToken();

  const connectionBuilder = useMemo(() => {
    return DbConnection.builder()
      .withUri('<YOUR SPACETIMEDB URL>')
      .withModuleName('<YOUR SPACETIMEDB MODULE NAME>')
      .withToken(token)
      .onConnect(onConnect)
      .onDisconnect(onDisconnect)
      .onConnectError(onConnectError);
  }, [token]);

  return (
    <SpacetimeDBProvider connectionBuilder={connectionBuilder}>
      <div>
        <h1>SpacetimeDB React App</h1>
        <p>
          You can now use SpacetimeDB in your app with Clerk authentication!
        </p>
      </div>
    </SpacetimeDBProvider>
  );
}
```

</StepCode>
</Step>

<Step title="Optional: Add basic signed-in UI and sign-out">
<StepText>
If you want quick UI controls, Clerk provides ready-made components such as `UserButton`.
</StepText>
<StepCode>

```tsx
import { UserButton } from '@clerk/clerk-react';

export function Header() {
  return (
    <header
      style={{ display: 'flex', justifyContent: 'flex-end', padding: 12 }}
    >
      <UserButton />
    </header>
  );
}
```

</StepCode>
</Step>

</StepByStep>

You are now set up to use **Clerk** authentication in your React application. When users access your app, they will be redirected to Clerk for sign-in, a session token (JWT) will be retrieved in the browser, and that token will be used to authenticate your SpacetimeDB connection.

If you are using **Clerk JWT templates** (recommended for controlling claims/audience/issuer), update the token retrieval line to:

```ts
await getToken({ template: '<YOUR_TEMPLATE_NAME>' });
```

and ensure your SpacetimeDB authentication layer validates the corresponding issuer and signing keys.
