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.