Skip to content
Torii docs

Control Components

Control components render their children based on the auth lifecycle. They don’t draw a surface of their own; each returns null (or a fallback) until its condition is met. They make it trivial to show different UI for signed-in and signed-out users.

All control components must be rendered inside <ToriiProvider>.

For gating on the runtime load lifecycle (the CDN/bundled UI bundle + its styles) rather than auth state, see runtime status.

ComponentRenders children when…
<SignedIn>Signed in and the session has no pending gates.
<SignedOut>Signed out (or a session gate is pending).
<AuthLoading>The initial session probe is still in flight.
<Show>Signed in, and (if when is given) the user has a matching role/permission.
<RedirectToSignIn>Signed out; navigates to your sign-in page.
<RedirectToGates>A session gate is pending; routes to its configured URL.

Renders its children only when the user is signed in and the session has cleared every authentication-critical gate (status === 'active').

A pending session (session.status === 'pending') renders as null, so protected UI never appears while a session gate is outstanding. A session is pending whenever an authentication gate has yet to clear: the user’s email is not yet verified, or legal consent has not been given (for example the legal-consent step during OAuth sign-up). While that gate is outstanding <SignedIn> (and <Show>) render nothing, and <SignedOut> renders its children so the gate-completion UI can show. Drive gate completion with gateUrls on <ToriiProvider>, render <RedirectToGates>, or read useSession().session.currentGate yourself.

import { SignedIn, UserButton } from '@torii-js/torii-react';
function Header() {
return (
<header>
<Logo />
<SignedIn>
<UserButton />
</SignedIn>
</header>
);
}
NameTypeDefaultDescription
childrenReactNode-Rendered when signed in with no pending gate.

Renders its children when the user is signed out or has a pending session gate. Pending counts as “not signed in for rendering purposes”, so your signed-out shell (typically hosting <SignIn> / <SignUp>) can also host the gate-completion UI.

import { SignedOut, SignIn } from '@torii-js/torii-react';
function Header() {
return (
<SignedOut>
<SignIn />
</SignedOut>
);
}
NameTypeDefaultDescription
childrenReactNode-Rendered when signed out or a gate is pending.

Use both together to swap content based on auth state:

import { ToriiProvider, SignedIn, SignedOut } from '@torii-js/torii-react';
function App() {
return (
<ToriiProvider publishableKey="pk_live_your-key">
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<SignIn />
</SignedOut>
</ToriiProvider>
);
}

Renders its children while the authentication state is being determined (useAuth().isLoading), e.g. while checking for an existing session cookie on page load. Use it to paint a skeleton before the gates decide.

import { AuthLoading, SignedIn, SignedOut } from '@torii-js/torii-react';
function App() {
return (
<ToriiProvider publishableKey="pk_live_your-key">
<AuthLoading>
<FullPageSpinner />
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<SignIn />
</SignedOut>
</ToriiProvider>
);
}
NameTypeDefaultDescription
childrenReactNode-Rendered while the initial session probe is in flight.

Render gate: renders children when the user is signed in and (if a when prop is given) satisfies the role/permission check; else renders fallback (or nothing). It also fires onUnauthorized in an effect once the session probe resolves and the check fails.

Like <SignedIn>, it excludes pending sessions; those count as not signed in for rendering.

This is a signed-in gate with optional role/permission authorization. Authorize with the when prop, or imperatively with useAuth().require({ role | permission }).

System permissions (org:sys_*) are not tokenized, so check them via { role }. The { permission } form is reserved for future custom permissions.

NameTypeDefaultDescription
childrenReactNode-Rendered when signed in and the when check (if any) passes.
whenAuthorizationParams-Optional role/permission requirement: { role: string } or { permission: string } (prefixed keys, e.g. { role: 'org:admin' }).
fallbackReactNode-Rendered instead of children when the check fails. Can be combined with onUnauthorized.
onUnauthorized() => void-Fired in an effect once the session is loaded and the check fails. Can be combined with fallback.
import { Show } from '@torii-js/torii-react';
function ProtectedPage() {
return (
<Show fallback={<LoginPrompt />}>
<SecretContent />
</Show>
);
}
import { Show } from '@torii-js/torii-react';
function AdminPanel() {
return (
<Show when={{ role: 'org:admin' }} fallback={<NotAllowed />}>
<DangerousSettings />
</Show>
);
}
import { Show, RedirectToSignIn } from '@torii-js/torii-react';
function ProtectedPage() {
return (
<Show fallback={<RedirectToSignIn signInUrl="/sign-in" />}>
<Dashboard />
</Show>
);
}

For logic outside a render gate, use useAuth().require(...), a role/permission authorization check. It returns a boolean:

import { useAuth } from '@torii-js/torii-react';
function Toolbar() {
const { require } = useAuth();
return (
<>
{require({ role: 'org:admin' }) && <DeleteButton />}
</>
);
}

Navigates the user to your sign-in page when they’re signed out. Waits for the initial session probe before firing, so already-signed-in users aren’t bounced. Honors ToriiProvider.navigate when set; otherwise does a full-page redirect. Renders null.

NameTypeDefaultDescription
signInUrlstring-Required. Path to your sign-in page (e.g. "/sign-in").
returnUrlstringCurrent path + searchValue for the appended returnUrl query param.
import { SignedOut, SignedIn, RedirectToSignIn } from '@torii-js/torii-react';
function ProtectedPage() {
return (
<>
<SignedOut>
<RedirectToSignIn signInUrl="/sign-in" />
</SignedOut>
<SignedIn>
<Dashboard />
</SignedIn>
</>
);
}

The current path is appended as returnUrl so you can route the user back after sign-in. Override it with the returnUrl prop:

<RedirectToSignIn signInUrl="/sign-in" returnUrl="/checkout" />

Routes a user with a pending session gate to the URL registered in <ToriiProvider gateUrls={…}>. Honors ToriiProvider.navigate when set; otherwise does a full-page redirect. Renders null.

When the current gate has no gateUrls entry it is a no-op but logs a one-shot console warning, so a misconfigured mapping surfaces in dev before it strands a user.

Don’t mount alongside <RedirectToSignIn>. Both fire navigation effects on the same render; if both qualify at once the destination is undefined. Render exactly one, gated on your own routing condition.

<RedirectToGates> takes no props.

import { SignedOut, RedirectToGates, SignIn } from '@torii-js/torii-react';
function AuthPage() {
return (
<SignedOut>
<RedirectToGates />
<SignIn />
</SignedOut>
);
}

For the signed-in user’s identity, use the useUser hook, or the full profile via useAuth().userProfile.

<ToriiProvider> resolves the session client-side from a cookie, so there’s no server-rendered auth state. During SSR (and the pre-probe first paint) the gates read the SSR-safe optimisticSignedIn snapshot, which is false on the server. So server-side <SignedIn>, <Show>, <RedirectToSignIn>, and <RedirectToGates> render nothing, but <SignedOut> renders its children and <AuthLoading> renders its children (because isLoading is true until the probe settles). On the client, before the probe settles, <SignedIn> / <SignedOut> paint from the session-hint cookie.

For a smoother first paint, wrap the signed-in surface in <AuthLoading> and render a skeleton while the cookie probe is in flight:

<AuthLoading>
<DashboardSkeleton />
</AuthLoading>
<SignedIn>
<Dashboard />
</SignedIn>
<SignedOut>
<RedirectToSignIn signInUrl="/sign-in" />
</SignedOut>
import type {
ControlComponentProps,
ShowProps,
RedirectToSignInProps,
} from '@torii-js/torii-react';