TL;DR: TanStack Start and TanStack Router provide a layered error handling system: route-level
errorComponent, global error boundaries, and server function try/catch patterns. This guide covers custom error pages per route, global error monitoring integration, graceful server errors with user-friendly messages, and error recovery flows.
Introduction
Error handling is often an afterthought, but it directly impacts user trust and retention. A cryptic "Something went wrong" message frustrates users. A well-designed error page with context, recovery options, and a way to contact support builds confidence.
TanStack Start provides multiple layers for error handling, from React error boundaries to server-side error mappings.
Route-Level Error Components
TanStack Router lets you define an errorComponent per route, giving you granular control over error presentation. Here's a dashboard route with a custom error component that distinguishes between auth errors and generic failures:
// src/routes/dashboard.tsx
import { createFileRoute } from '@tanstack/react-router'
import { ErrorComponent } from '@tanstack/react-router'
export const Route = createFileRoute('/dashboard')({
component: DashboardPage,
errorComponent: ({ error, reset }) => (
<div className="p-8 text-center">
<h2 className="text-xl font-bold mb-2">Dashboard Error</h2>
<p className="text-gray-600 mb-4">
{error instanceof AuthError
? 'Your session expired. Please log in again.'
: 'Unable to load dashboard. Please try again.'}
</p>
<div className="flex gap-4 justify-center">
<button onClick={reset} className="btn-primary">Retry</button>
<a href="/login" className="btn-secondary">Log In</a>
</div>
<details className="mt-4 text-xs text-gray-400">
<summary>Technical Details</summary>
<pre>{error.message}</pre>
</details>
</div>
),
})
The errorComponent receives both the error object and a reset function. The reset function clears the error boundary and re-renders the route, giving users a recovery path without navigating away. Categorizing errors (auth vs. generic) lets you show context-appropriate messages.
Global Error Boundary
For errors that escape route-level handling, set up a global error boundary in your root route. This catches any unhandled errors across all child routes:
// src/routes/__root.tsx
import { createRootRoute, Outlet } from '@tanstack/react-router'
export const Route = createRootRoute({
component: () => (
<ErrorBoundary fallback={<GlobalErrorPage />}>
<Outlet />
</ErrorBoundary>
),
})
The global error boundary acts as a safety net. If a route-level errorComponent fails or an unexpected error propagates, the global boundary renders a fallback UI that covers the entire application.
Server Function Error Handling
Server functions should catch and classify errors before they reach the client. Here's a pattern that differentiates database errors from other failures, logging critical issues while returning user-friendly messages:
export const fetchDataFn = createServerFn({ method: 'GET' })
.handler(async () => {
try {
return await loadData()
} catch (error) {
if (error instanceof DatabaseError) {
// Log for monitoring
console.error('Database error:', error)
// Return user-friendly response
return { error: 'Data temporarily unavailable', retryAfter: 30 }
}
throw error // Let middleware handle it
}
})
This approach keeps your client-side code clean: the server function either returns data or a structured error response, and the client handles each case accordingly without parsing error messages.
Error Handling Strategy Table
| Layer | Catches | User Sees | Recovery |
|---|---|---|---|
| Zod validation | Invalid input | Field error | Fix input |
| Server function | Business logic | Error message + retry | Retry or contact |
| Route errorComponent | Route loading error | Custom error page | Retry or navigate |
| Global ErrorBoundary | Any uncaught error | Fallback page | Refresh or support |
onError (mutation) | Mutation failure | Toast | Rollback UI |
Conclusion
TanStack Start's layered error handling gives you control over every failure mode. Route-level error components provide context-specific recovery, while global boundaries catch anything that slips through. For deeper integration, combine error boundaries with Middleware and Request Lifecycle patterns, pair them with Nested Layouts and Auth Guards for protected routes, and wire up SaaS Monitoring and Observability for production error tracking.