SDKsTypeScript SDKGuides
React Integration
Integrate Avnology ID into React applications with hooks, protected routes, and session management.
React Integration
This guide shows how to integrate the TypeScript SDK with React 19 and React Router 7. For pre-built UI components (login form, registration form, MFA challenge), use @avnology/id-elements alongside the SDK.
Setup
npm install @avnology/sdk-typescript @avnology/id-elementsCreate a shared client instance
// lib/avnology.ts
import { AvnologyId } from "@avnology/sdk-typescript";
export const avnology = new AvnologyId({
baseUrl: import.meta.env.VITE_AVNOLOGY_BASE_URL,
clientId: import.meta.env.VITE_AVNOLOGY_CLIENT_ID,
credentials: "include",
autoRefresh: true,
});Provider setup
// root.tsx
import { AvnologyIdProvider } from "@avnology/id-elements";
import { avnology } from "./lib/avnology";
export default function Root() {
return (
<AvnologyIdProvider client={avnology}>
<Outlet />
</AvnologyIdProvider>
);
}Authentication hooks
useSession()
Access the current session in any component.
import { useSession } from "@avnology/id-elements";
function Dashboard() {
const { session, isLoading, error } = useSession();
if (isLoading) return <LoadingSpinner />;
if (!session) return <Redirect to="/login" />;
return (
<div>
<h1>Welcome, {session.identity.name?.first}</h1>
<p>{session.identity.email}</p>
</div>
);
}useLogout()
import { useLogout } from "@avnology/id-elements";
function LogoutButton() {
const { logout, isLoading } = useLogout();
return (
<button onClick={() => logout()} disabled={isLoading}>
{isLoading ? "Logging out..." : "Log out"}
</button>
);
}Protected routes
Route guard with React Router loader
// lib/route-guards.ts
import { redirect } from "react-router";
import { avnology } from "./avnology";
export async function requireAuth() {
const session = await avnology.getSession();
if (!session) {
throw redirect("/login");
}
return { session };
}
export async function requireMfa() {
const session = await avnology.getSession();
if (!session) {
throw redirect("/login");
}
if (session.authenticatorAssuranceLevel !== "aal2") {
throw redirect("/mfa");
}
return { session };
}Using the guard in routes
// routes/dashboard.tsx
import { requireAuth } from "../lib/route-guards";
export async function loader() {
return requireAuth();
}
export default function DashboardPage() {
const { session } = useLoaderData<typeof loader>();
return <Dashboard user={session.identity} />;
}Permission-gated UI
import { usePermission } from "@avnology/id-elements";
function ProjectSettings({ projectId }: { projectId: string }) {
const { allowed, isLoading } = usePermission({
subject: `user:${userId}`,
relation: "admin",
object: `project:${projectId}`,
});
if (isLoading) return <LoadingSpinner />;
if (!allowed) return <AccessDenied />;
return <SettingsForm />;
}Pre-built UI components
@avnology/id-elements provides complete auth UI components that handle the full flow.
import { LoginForm, RegistrationForm, MfaChallenge } from "@avnology/id-elements";
// Login page
function LoginPage() {
return (
<LoginForm
onSuccess={(session) => navigate("/dashboard")}
onMfaRequired={(flowId) => navigate(`/mfa?flow=${flowId}`)}
showSocialProviders={["google", "github"]}
showPasskeyOption
/>
);
}
// Registration page
function RegisterPage() {
return (
<RegistrationForm
onSuccess={(session) => navigate("/onboarding")}
fields={["email", "password", "name"]}
/>
);
}
// MFA challenge page
function MfaPage() {
const [searchParams] = useSearchParams();
return (
<MfaChallenge
flowId={searchParams.get("flow")!}
onSuccess={(session) => navigate("/dashboard")}
/>
);
}See also
- Next.js guide -- Next.js App Router integration
- Testing guide -- Testing with mocks
- Session management -- Session methods