React SDK
The @passkeybase/react package provides hooks, components, and route guards for integrating passkey authentication into React applications.
Installation
npm install @passkeybase/react @passkeybase/jsProvider Setup
Wrap your app with PasskeyBaseProviderat the root. This initializes the SDK and makes auth state available to all child components via React Context.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PasskeyBaseProvider } from '@passkeybase/react';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<PasskeyBaseProvider
clientId="pk_live_abc123"
domain="myapp.auth.ing"
redirectUri={window.location.origin + '/callback'}
>
<App />
</PasskeyBaseProvider>
);Auth Hooks
useAuth()
Access authentication state and methods from any component inside the provider.
import { useAuth } from '@passkeybase/react';
function Dashboard() {
const {
isAuthenticated,
isLoading,
loginWithRedirect,
logout,
getAccessToken,
} = useAuth();
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <button onClick={() => loginWithRedirect()}>Log in</button>;
}
const handleApiCall = async () => {
const token = await getAccessToken();
const res = await fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` },
});
const data = await res.json();
console.log(data);
};
return (
<div>
<h1>Welcome back</h1>
<button onClick={handleApiCall}>Fetch data</button>
<button onClick={() => logout()}>Log out</button>
</div>
);
}useUser()
Access the current user's profile. Returns undefined when not authenticated.
import { useUser } from '@passkeybase/react';
function Profile() {
const { user, isLoading } = useUser();
if (isLoading) return <div>Loading profile...</div>;
if (!user) return null;
return (
<div>
<h2>{user.displayName}</h2>
<p>{user.email}</p>
<p>User ID: {user.sub}</p>
<p>Passkeys registered: {user.credentials.length}</p>
</div>
);
}Protected Routes
The AuthGuard component redirects unauthenticated users to the login page. It accepts an optional fallback prop shown while the auth check is in progress.
import { AuthGuard } from '@passkeybase/react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/callback" element={<CallbackPage />} />
<Route
path="/dashboard"
element={
<AuthGuard fallback={<div>Redirecting to login...</div>}>
<Dashboard />
</AuthGuard>
}
/>
<Route
path="/settings"
element={
<AuthGuard>
<Settings />
</AuthGuard>
}
/>
</Routes>
</BrowserRouter>
);
}Login and Logout
Pre-built Buttons
Drop-in LoginButton andLogoutButton components. They accept all standard button props for styling.
import { LoginButton, LogoutButton } from '@passkeybase/react';
function Header() {
const { isAuthenticated } = useAuth();
return (
<nav>
<h1>My App</h1>
{isAuthenticated ? (
<LogoutButton className="btn btn-secondary">
Sign out
</LogoutButton>
) : (
<LoginButton className="btn btn-primary">
Sign in with Passkey
</LoginButton>
)}
</nav>
);
}Programmatic Login
Use the useAuth() hook for full control over the login and logout flow.
import { useAuth } from '@passkeybase/react';
function CustomLogin() {
const { loginWithRedirect, logout } = useAuth();
const handleLogin = () => {
loginWithRedirect({
scopes: ['openid', 'profile', 'email'],
state: 'return=/onboarding',
});
};
const handleLogout = () => {
logout({ returnTo: window.location.origin });
};
return (
<div>
<button onClick={handleLogin}>Log in</button>
<button onClick={handleLogout}>Log out</button>
</div>
);
}Handling the Callback
Create a callback page that calls handleRedirectCallback() and then navigates the user into the app.
import { useAuth } from '@passkeybase/react';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
function CallbackPage() {
const { handleRedirectCallback, isLoading } = useAuth();
const navigate = useNavigate();
useEffect(() => {
const handle = async () => {
const result = await handleRedirectCallback();
const returnTo = result.state || '/dashboard';
navigate(returnTo);
};
handle();
}, []);
return <div>Completing login...</div>;
}Passkey Enrollment
The useEnrollment() hook manages the passkey enrollment flow. It provides loading state and error handling out of the box.
import { useEnrollment, useAuth } from '@passkeybase/react';
function PasskeySettings() {
const { getAccessToken } = useAuth();
const { enrollPasskey, isEnrolling, error } = useEnrollment();
const handleEnroll = async () => {
// Fetch an enrollment token from your backend
const token = await getAccessToken();
const res = await fetch('/api/enrollment-token', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
});
const { enrollmentToken } = await res.json();
// Open the enrollment popup
await enrollPasskey(enrollmentToken);
alert('Passkey added successfully!');
};
return (
<div>
<h2>Passkey Management</h2>
<button onClick={handleEnroll} disabled={isEnrolling}>
{isEnrolling ? 'Enrolling...' : 'Add a new passkey'}
</button>
{error && <p className="error">{error.message}</p>}
</div>
);
}TypeScript Support
All hooks, components, and props are fully typed. Types are re-exported from @passkeybase/js for convenience.
import type { User, AuthResult } from '@passkeybase/js';
import { useAuth, useUser } from '@passkeybase/react';
// All hooks are fully typed
const { user } = useUser(); // user: User | undefined
const { isAuthenticated } = useAuth(); // isAuthenticated: boolean
// Provider props are typed via PasskeyBaseProviderProps
interface PasskeyBaseProviderProps {
clientId: string;
domain: string;
redirectUri?: string;
scopes?: string[];
audience?: string;
cacheLocation?: 'memory' | 'localstorage';
children: React.ReactNode;
}Next steps
- JavaScript SDK -- core API reference
- Next.js SDK -- server-side rendering and API routes
- Node.js SDK -- generate enrollment tokens on the server