_archiveGuides
Protected Routes
Create routes that require authentication.
Docs are in beta — content is improving rapidly. Found something missing? Open an issue on GitHub or reach out on Twitter.
Overview
Protected routes require users to be authenticated before accessing them. BetterStarter uses file-based routing with an underscore convention for protected routes.
Creating a Protected Route
Use the _ prefix in your route file to denote it as protected:
File Structure
src/routes/
profile/
__root.tsx # Layout
index.tsx # Public profile list (/profile/)
$_/ # Protected routes group
settings.tsx # /profile/settings (requires auth)
edit.tsx # /profile/edit (requires auth)Route Example
// src/routes/profile/$_/settings.tsx
import { createFileRoute, notFound } from '@tanstack/react-router'
import { authMiddleware } from '@/lib/auth/middleware'
const loader = async (opts) => {
const user = await getAuthUser(opts)
if (!user) {
throw notFound()
}
return { user }
}
export const Route = createFileRoute('/profile/$_/settings')({
loader,
component: SettingsView,
})
function SettingsView() {
const { user } = Route.useLoaderData()
return <div>Settings for {user.name}</div>
}Accessing User Context
In your route loader or component:
import { Route } from '@tanstack/react-router'
function MyComponent() {
const context = Route.useMatch()
const user = context?.context?.user
if (!user) {
return <div>Not authenticated</div>
}
return <div>Hello, {user.name}</div>
}Redirect on Auth Required
For routes that should redirect unauthenticated users:
import { redirect } from '@tanstack/react-router'
const loader = async () => {
const user = await getCurrentUser()
if (!user) {
throw redirect({ to: '/auth/signin' })
}
return { user }
}Pattern: Role-Based Access
const loader = async () => {
const user = await getCurrentUser()
if (!user || user.role !== 'admin') {
throw notFound()
}
return { user }
}Testing Protected Routes
Use the route directly in tests:
test('redirects to signin when not authenticated', async () => {
const loader = Route.options.loader
expect(() => loader({ params: {} })).toThrow(redirect)
})