React SDK
The React SDK (@reauth-dev/sdk/react) provides hooks and components for React and Next.js apps.
Installation
npm install @reauth-dev/sdk
React is a peer dependency (>= 18.0.0).
Exports
import {
AuthProvider,
useAuthContext,
useAuth,
useHeadlessAuth,
ProtectedRoute,
} from '@reauth-dev/sdk/react';
AuthProvider + useAuthContext
The recommended pattern for Next.js App Router apps. Wrap your app in AuthProvider and access auth state anywhere with useAuthContext.
// app/providers.tsx
'use client';
import { AuthProvider } from '@reauth-dev/sdk/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<AuthProvider config={{ domain: 'yourdomain.com' }}>
{children}
</AuthProvider>
);
}
// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
// Any component inside AuthProvider
'use client';
import { useAuthContext } from '@reauth-dev/sdk/react';
export function UserMenu() {
const { user, logout } = useAuthContext();
return (
<div>
{user?.email}
<button onClick={logout}>Sign out</button>
</div>
);
}
AuthProvider Props
type AuthProviderProps = {
config: {
domain: string; // Your verified domain
refreshInterval?: number; // Session check interval in ms (default: 300000 / 5 min)
timeout?: number; // Request timeout in ms (default: 10000)
};
children: ReactNode;
};
useAuthContext Return Value
{
user: User | null; // Authenticated user, or null
loading: boolean; // True during initial session check
error: string | null; // Error message if auth check failed
isOnWaitlist: boolean; // True if user is on waitlist
waitlistPosition: number | null;
login: () => void; // Redirect to hosted login page
logout: () => Promise<void>; // Clear session and reset state
refetch: () => Promise<void>; // Manually re-check session
}
The User type:
type User = {
id: string;
email: string;
roles: string[];
activeOrgId: string;
orgRole: string; // "owner" | "member"
};
useAuth (Standalone Hook)
If you don't want to use context, useAuth provides the same functionality as a standalone hook:
'use client';
import { useAuth } from '@reauth-dev/sdk/react';
export default function Page() {
const { user, loading, login, logout } = useAuth({
domain: 'yourdomain.com',
refreshInterval: 60000, // Check every minute
});
if (loading) return <div>Loading...</div>;
if (!user) return <button onClick={login}>Sign in</button>;
return (
<div>
Welcome {user.email}
<button onClick={logout}>Sign out</button>
</div>
);
}
Returns the same shape as useAuthContext.
ProtectedRoute
Wraps content that requires authentication. Automatically redirects unauthenticated users to the login page.
import { ProtectedRoute } from '@reauth-dev/sdk/react';
// Basic usage — redirects to hosted login if not authenticated
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
// With loading fallback
<ProtectedRoute fallback={<LoadingSpinner />}>
<Dashboard />
</ProtectedRoute>
// With custom handlers
<ProtectedRoute
onUnauthenticated={() => router.push('/login')}
onWaitlist={() => router.push('/waitlist')}
>
<Dashboard />
</ProtectedRoute>
Props
type ProtectedRouteProps = {
children: ReactNode;
fallback?: ReactNode; // Shown while loading (default: null)
onUnauthenticated?: () => void; // Custom redirect (default: calls login())
onWaitlist?: () => void; // Custom waitlist handler
};
useHeadlessAuth
For building custom login UIs without the hosted login page. Requires headless mode to be enabled in the dashboard.
'use client';
import { useHeadlessAuth } from '@reauth-dev/sdk/react';
export default function LoginPage() {
const {
requestMagicLink,
verifyMagicLink,
startGoogleOAuth,
startTwitterOAuth,
loading,
error,
step,
config,
getConfig,
reset,
} = useHeadlessAuth({
domain: 'yourdomain.com',
callbackUrl: 'https://yourdomain.com/auth/verify',
});
if (step === 'magic_link_sent') {
return <p>Check your email for a magic link!</p>;
}
if (step === 'completed') {
return <p>Authenticated! Redirecting...</p>;
}
return (
<form onSubmit={async (e) => {
e.preventDefault();
const email = new FormData(e.currentTarget).get('email') as string;
await requestMagicLink(email);
}}>
<input name="email" type="email" placeholder="Enter your email" />
<button type="submit" disabled={loading}>Send Magic Link</button>
<button type="button" onClick={startGoogleOAuth} disabled={loading}>
Sign in with Google
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}
Verification Page
// app/auth/verify/page.tsx
'use client';
import { useEffect } from 'react';
import { useHeadlessAuth } from '@reauth-dev/sdk/react';
export default function VerifyPage() {
const { verifyMagicLink, step, error } = useHeadlessAuth({
domain: 'yourdomain.com',
});
useEffect(() => {
const token = new URLSearchParams(window.location.search).get('token');
if (token) {
verifyMagicLink(token).then((result) => {
if (result.success) {
window.location.href = result.redirectUrl || '/dashboard';
}
});
}
}, []);
if (error) return <p>Verification failed: {error}</p>;
return <p>Verifying...</p>;
}
Options
type UseHeadlessAuthOptions = {
domain: string;
callbackUrl?: string; // URL for magic link emails to point to
timeout?: number; // Request timeout in ms
};
Return Value
{
loading: boolean;
error: string | null;
step: 'idle' | 'magic_link_sent' | 'google_started' | 'twitter_started' | 'completed';
config: DomainConfig | null;
verifyResult: MagicLinkVerifyResult | null;
getConfig: () => Promise<DomainConfig>;
requestMagicLink: (email: string) => Promise<void>;
verifyMagicLink: (token: string) => Promise<MagicLinkVerifyResult>;
startGoogleOAuth: () => Promise<GoogleOAuthStartResult>;
startTwitterOAuth: () => Promise<TwitterOAuthStartResult>;
reset: () => void; // Reset to idle state
}
Token Management for API Calls
To call your own protected API endpoints, use getToken() from the browser client:
'use client';
import { createReauthClient } from '@reauth-dev/sdk';
import { useAuthContext } from '@reauth-dev/sdk/react';
const reauth = createReauthClient({ domain: 'yourdomain.com' });
export function useFetchWithAuth() {
const { user } = useAuthContext();
return async (url: string, options?: RequestInit) => {
const tokenResponse = await reauth.getToken();
if (!tokenResponse) throw new Error('Not authenticated');
return fetch(url, {
...options,
headers: {
...options?.headers,
Authorization: `Bearer ${tokenResponse.accessToken}`,
},
});
};
}
Alternatively, if your API and auth share the same parent domain, session cookies are sent automatically with credentials: 'include'.