Svelte

@robelest/convex-auth/svelte bridges an app-owned browser auth client into a Svelte 5 runes object, shared through context, plus gate components. Use it in SvelteKit and Vite Svelte apps running Svelte 5.

svelte is an optional peer dependency — if your app uses this subpath, you already have Svelte installed. Apps that only consume the server entrypoints don’t pay for Svelte.

Setup

Call setupConvexAuth once in your root layout, passing a browser auth client created with the same Convex client you give setupConvex. The Svelte binding subscribes to the client and shares the reactive auth through context; the app still owns the client’s lifetime.

<!-- +layout.svelte -->
<script lang="ts">
  import { page } from "$app/state";
  import { setupConvex, useConvexClient } from "convex-svelte";
  import { onDestroy } from "svelte";
  import { client as createAuthClient } from "@robelest/convex-auth/browser";
  import { setupConvexAuth } from "@robelest/convex-auth/svelte";
  import { api } from "$convex/_generated/api.js";

  let { children } = $props();

  setupConvex(import.meta.env.VITE_CONVEX_URL);
  const authClient = createAuthClient({
    convex: useConvexClient(),
    api: api.auth,
    location: () => page.url,
  });
  const auth = setupConvexAuth(authClient);
  onDestroy(() => authClient.destroy());
</script>

{#if auth.signedIn}
  {@render children()}
{:else}
  <Login />
{/if}

auth is reactive: read auth.signedIn, auth.signedOut, auth.loading, auth.status, and auth.token directly in markup — no $state or subscribe of your own. Because the browser client boots synchronously from persisted storage, a returning user can be signedIn on the first paint. Fresh sign-in and refresh tokens report loading until Convex confirms them.

useConvexAuth()

Read the same reactive auth from any descendant component. No prop drilling.

<script lang="ts">
  import { useConvexAuth } from "@robelest/convex-auth/svelte";

  const auth = useConvexAuth();
</script>

<button onclick={() => auth.signOut()}>Sign out</button>

auth.signIn and auth.signOut are the client’s actions; auth.token is the JWT when signed in and null otherwise.

Gate components

Prefer {#if auth.signedIn} when you already have auth. The gates are for declarative wrapping and mirror the React binding. <SignedIn> passes the JWT (typed string) to its children snippet. <AuthLoading> is optional — a synchronous-storage SPA effectively never hits it.

<script lang="ts">
  import { SignedIn, SignedOut, AuthLoading, useConvexAuth } from "@robelest/convex-auth/svelte";

  const auth = useConvexAuth();
</script>

<AuthLoading>
  <span>Loading…</span>
</AuthLoading>
<SignedOut>
  <button onclick={() => auth.signIn("google")}>Sign in with Google</button>
</SignedOut>
<SignedIn>
  {#snippet children(token)}
    <Dashboard {token} />
  {/snippet}
</SignedIn>

auth.client

The underlying imperative client, for factor flows (totp, passkey, device) and low-level methods (completeOAuth, param, initialize).

<script lang="ts">
  import { useConvexAuth } from "@robelest/convex-auth/svelte";

  const auth = useConvexAuth();
</script>

<button onclick={() => auth.client.totp?.setup()}>Enable TOTP</button>

client.totp, client.passkey, client.device are present only when the underlying providers are configured server-side.

SSR

Create the browser auth client with the server-known JWT via the token option, then pass that client to setupConvexAuth. See SSR overview and SvelteKit.