Execution contract
- Use when
- Use when a Router subtree requires a valid signed-in session before any child loader runs.
- Avoid when
- Do not treat client beforeLoad as server resource authorization or rely on hidden navigation links.
- Preconditions
- Identify how auth reaches typed Router Context, the common parent of private routes, and the allowed post-login destinations.
- Verification
- Test signed-out direct entry, signed-in refresh, session expiry, and a malicious external return URL; prove child loaders and server entries independently reject unauthorized access.
- Failure mode
- If private data loads before redirect, the gate is too low or async auth was not completed in the parent beforeLoad; move it to the nearest common ancestor.
- Security
- Validate return targets as safe same-site paths to prevent open redirects; every private Server Function, Server Route, and data source must authenticate and authorize again.
Decision order
First identify how auth state reaches Router Context, then choose the nearest pathless layout that owns the protected subtree. Parent beforeLoad runs before children; throw redirect without a session and return the smallest user context with one. Do not duplicate the check in every leaf route.
Minimal implementation
Record the actual destination from the beforeLoad location.href. The login handler must allow only relative same-site paths or verified same-origin targets, falling back to a fixed home route for external URLs.
import { Outlet, createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/_authenticated')({
beforeLoad: ({ context, location }) => {
if (!context.auth.user) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
return { user: context.auth.user }
},
component: () => <Outlet />,
})Verification matrix
Verify signed-out direct entry never runs child loaders, signed-in refresh works, an expired session redirects on the next navigation, external redirects are rejected, and direct calls that bypass the UI still receive an independent unauthorized response from the server boundary.