Skip to content

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.

Start with a prompt

Hand this to your AI coding assistant to scaffold the Torii setup, then follow the steps below to fill in the details.

Set up Torii authentication in my React app using the @torii-js/torii-react SDK.

1. Install it: npm install @torii-js/torii-react
2. Add my publishable key to .env.local as VITE_TORII_PUBLISHABLE_KEY (I will paste the pk_test_… value from app.torii.so → Settings → API keys).
3. Wrap the app root in <ToriiProvider publishableKey={import.meta.env.VITE_TORII_PUBLISHABLE_KEY}>.
4. In the main view, render the control components: <AuthLoading> for the loading state, <SignedOut> containing <SignIn />, and <SignedIn> containing <UserDashboard />.
5. Remind me to add my dev origin (e.g. http://localhost:5173) to Allowed origins in the Torii dashboard so browser requests pass CORS.

Follow the reference guide: https://docs.torii.so/guides/getting-started/quick-start
  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.