Building with the SDK
TanStack Start
ZevAuth in a TanStack Start app, with route guards on the client and verified requests on the server.
TanStack Start uses @zevauth/react for everything that
renders, and @zevauth/backend/verify for
server routes and server functions.
npm install @zevauth/react @zevauth/js @zevauth/backend
Where the session lives
The session is held by the browser and sent as a bearer token by your own requests. It isn’t a cookie, so a server-rendered page request carries no token. Guard routes in the browser, and verify the token on the server wherever your data is read.
One client for the app
// src/zevauth.ts
import { createZevAuth } from '@zevauth/js';
export const zevauth = createZevAuth({
publishableKey: import.meta.env.VITE_ZEVAUTH_KEY,
});
// src/routes/__root.tsx
import { ZevAuthProvider } from '@zevauth/react';
import { zevauth } from '../zevauth';
function RootComponent() {
return (
<ZevAuthProvider client={zevauth}>
<Outlet />
</ZevAuthProvider>
);
}
Protecting routes
beforeLoad runs on the server during the first render and in the browser
on every navigation after it. Check for a token only in the browser:
// src/routes/_signed-in.tsx: a layout route for everything behind sign-in
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router';
import { SignedIn } from '@zevauth/react';
import { zevauth } from '../zevauth';
export const Route = createFileRoute('/_signed-in')({
beforeLoad: async ({ location }) => {
if (typeof window === 'undefined') return;
if (!(await zevauth.getToken())) {
throw redirect({ to: '/sign-in', search: { redirect_url: location.href } });
}
},
// On the server render, show nothing until the browser knows.
component: () => (
<SignedIn>
<Outlet />
</SignedIn>
),
});
<SignedIn> renders nothing during the server render and until the session
has loaded, so a signed-out visitor never sees the protected page flash
before the redirect.
Verifying on the server
Server routes and server functions both have the incoming Request. Pass it
to getAuth:
import { getAuth } from '@zevauth/backend/verify';
const auth = await getAuth(request, {
environmentId: process.env.ZEVAUTH_ENVIRONMENT_ID!,
});
if (!auth.isSignedIn) {
throw new Response('Sign in to continue.', { status: 401 });
}
In a server function, read the request with TanStack Start’s request
helper (getRequest() in current releases, getWebRequest() in older
ones). Then send the token from the browser when you call it:
const token = await zevauth.getToken();
await listProjects({ headers: { Authorization: `Bearer ${token}` } });
getAuth gives you userId, organizationId, organizationRole and
has({ role, permission }). It never throws: a missing, expired or tampered
token reads as signed out.
Updated at, Sunday, November 1, 2026