Guides
Protected Routes
Create routes that require authentication.
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)
})