# err-not-found: Handle Not-Found Routes Properly ## Priority: HIGH ## Explanation Configure `notFoundComponent` to handle 404 errors gracefully. TanStack Router provides not-found handling at multiple levels: root, route-specific, and programmatic via `notFound()`. Proper configuration prevents blank screens and improves UX. ## Bad Example ```tsx // No not-found handling - shows blank screen or error const router = createRouter({ routeTree, // Missing defaultNotFoundComponent }) // Or throwing generic error export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params }) => { const post = await fetchPost(params.postId) if (!post) { throw new Error('Not found') // Generic error, not proper 404 } return post }, }) ``` ## Good Example: Root-Level Not Found ```tsx // routes/__root.tsx export const Route = createRootRoute({ component: RootComponent, notFoundComponent: GlobalNotFound, }) function GlobalNotFound() { return (

404 - Page Not Found

The page you're looking for doesn't exist.

Go Home
) } // router.tsx - Can also set default const router = createRouter({ routeTree, defaultNotFoundComponent: () => (

404

Return Home
), }) ``` ## Good Example: Route-Specific Not Found ```tsx // routes/posts/$postId.tsx import { createFileRoute, notFound } from '@tanstack/react-router' export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params }) => { const post = await fetchPost(params.postId) if (!post) { throw notFound() // Proper 404 handling } return post }, notFoundComponent: PostNotFound, // Custom 404 for this route component: PostPage, }) function PostNotFound() { const { postId } = Route.useParams() return (

Post Not Found

No post exists with ID: {postId}

Browse all posts
) } ``` ## Good Example: Not Found with Data ```tsx export const Route = createFileRoute('/users/$username')({ loader: async ({ params }) => { const user = await fetchUser(params.username) if (!user) { throw notFound({ // Pass data to notFoundComponent data: { username: params.username, suggestions: await fetchSimilarUsernames(params.username), }, }) } return user }, notFoundComponent: UserNotFound, }) function UserNotFound() { const { data } = Route.useMatch() return (

User @{data?.username} not found

{data?.suggestions?.length > 0 && (

Did you mean:

)}
) } ``` ## Good Example: Catch-All Route ```tsx // routes/$.tsx - Catch-all splat route export const Route = createFileRoute('/$')({ component: CatchAllNotFound, }) function CatchAllNotFound() { const { _splat } = Route.useParams() return (

Page Not Found

No page exists at: /{_splat}

Go to homepage
) } ``` ## Good Example: Nested Not Found Bubbling ```tsx // Not found bubbles up through route tree // routes/posts.tsx export const Route = createFileRoute('/posts')({ notFoundComponent: PostsNotFound, // Catches child 404s too }) // routes/posts/$postId.tsx export const Route = createFileRoute('/posts/$postId')({ loader: async ({ params }) => { const post = await fetchPost(params.postId) if (!post) throw notFound() return post }, // No notFoundComponent - bubbles to parent }) // routes/posts/$postId/comments.tsx export const Route = createFileRoute('/posts/$postId/comments')({ loader: async ({ params }) => { const comments = await fetchComments(params.postId) if (!comments) throw notFound() // Bubbles to /posts notFoundComponent return comments }, }) ``` ## Context - `notFound()` throws a special error caught by nearest `notFoundComponent` - Not found bubbles up the route tree if not handled locally - Use `defaultNotFoundComponent` on router for global fallback - Pass data to `notFound({ data })` for contextual 404 pages - Catch-all routes (`/$`) can handle truly unknown paths - Different from error boundaries - specifically for 404 cases