Skip to content
Torii docs

React Quickstart

Add Torii authentication to a React app. This guide uses Vite; the same steps work for any React framework; only the environment-variable plumbing changes.

  1. Sign up at app.torii.so and click New application. Name it after your app. From Settings → API keys, copy the publishable key; it looks like pk_test_… in sandbox and pk_live_… in production.

    Under Settings → Allowed origins, add http://localhost:5173 (or whatever port your dev server uses). Without this the API rejects browser requests with a CORS error.

  2. Terminal window
    bun add @torii-js/torii-react
  3. Create or open .env.local at the project root and add your publishable key:

    .env.local
    VITE_TORII_PUBLISHABLE_KEY=pk_test_your-publishable-key

    Vite exposes any variable prefixed with VITE_ to client code. Other frameworks have similar conventions: NEXT_PUBLIC_… for Next.js, PUBLIC_… for Astro, etc.

  4. Wrap your root with <ToriiProvider> and pass the publishable key. The provider owns session state, refreshes JWTs in the background, and exposes the useAuth() hook to the rest of the tree.

    src/main.tsx
    import { ToriiProvider } from '@torii-js/torii-react';
    import { StrictMode } from 'react';
    import { createRoot } from 'react-dom/client';
    import App from './App';
    const PUBLISHABLE_KEY = import.meta.env.VITE_TORII_PUBLISHABLE_KEY;
    if (!PUBLISHABLE_KEY) {
    throw new Error('Missing VITE_TORII_PUBLISHABLE_KEY');
    }
    createRoot(document.getElementById('root')!).render(
    <StrictMode>
    <ToriiProvider publishableKey={PUBLISHABLE_KEY}>
    <App />
    </ToriiProvider>
    </StrictMode>,
    );
  5. <AuthLoading>, <SignedOut>, and <SignedIn> are control components; each renders its children for exactly one phase of the auth lifecycle. On every page load the provider runs a session probe (the cookie is checked against the server); these three tags cover all three outcomes:

    • <AuthLoading>: the probe is still in flight. Render a spinner or skeleton. Skipping it means signed-in users briefly see the sign-in card flash on reload before the probe resolves.
    • <SignedOut>: no session. Render <SignIn> (the prebuilt card).
    • <SignedIn>: authenticated. Render <UserDashboard>, the full account surface. It bundles the <UserButton> avatar menu, an email-verification banner, and the profile panel: editable profile fields, connected accounts (account linking), active sessions, and data-export requests. Drop in <UserButton> on its own instead if you only want the avatar menu.
    src/App.tsx
    import { AuthLoading, SignedIn, SignedOut, SignIn, UserDashboard } from '@torii-js/torii-react';
    export default function App() {
    return (
    <main>
    <AuthLoading>
    <p>Loading…</p>
    </AuthLoading>
    <SignedOut>
    <SignIn />
    </SignedOut>
    <SignedIn>
    <UserDashboard />
    </SignedIn>
    </main>
    );
    }
  6. Run the dev server:

    Terminal window
    bun run dev

    Open http://localhost:5173; the sign-in card renders for signed-out users. Click Sign up, create an account, verify the email link from Mailpit / your inbox, and you’ll see the <UserDashboard> appear in its place.

Components

Full prop reference for <SignIn>, <SignUp>, <UserButton>, <UserDashboard>, and <UserProfile>. Browse →

Customize the theme

Pick a preset (shadcn / mui), override CSS tokens, or attach per-element classes via appearance.elements. Theming →

i18n & labels

Ship in en and da out of the box, or override every label. Labels →

Hooks

useAuth(), useUser(), and useAuthFetch() for reading auth state and calling your API. Hooks →

First-party cookies (required)

Before production, wire up a proxy or CNAME so Safari doesn’t silently sign your users out. Set up →

”Failed to fetch” or CORS errors in the console

Section titled “”Failed to fetch” or CORS errors in the console”

Your dev origin isn’t in the application’s allowed-origins list. Open the dashboard → Settings → Allowed origins and add the exact URL the browser sends from (scheme + host + port, no trailing slash). http://localhost:5173 and http://127.0.0.1:5173 are different; add the one the browser is actually using.

<SignIn> renders but submit returns 401 Unauthorized

Section titled “<SignIn> renders but submit returns 401 Unauthorized”

Almost always one of:

  1. The publishable key doesn’t match the application you set up in the dashboard. Re-copy it from Settings → API keys.
  2. You’re using a pk_live_… key against a sandbox environment, or vice-versa. Use the pk_test_… key during local development.
  3. The user exists but hasn’t verified their email yet. Check Users in the dashboard.

useAuth must be used inside <ToriiProvider> thrown at runtime

Section titled “useAuth must be used inside <ToriiProvider> thrown at runtime”

A component calling useAuth, useAuthFetch, or any control component (<SignedIn>, <SignedOut>, <Show>) is rendering outside the provider. In Next.js this usually means you need a 'use client' wrapper around <ToriiProvider> imported into the root layout.

If something else is going wrong, mail hello@torii.so with the error and a code snippet, and we’ll respond inside one business day.