Routing
Errors and 404
Answer 404 from a VitNode route loader with notFound(), and configure the app's localized 404 and 500 pages, their action buttons and the development error overlay.
VitNode ships localized error screens that render inside your app's layout, so a visitor who hits a missing page or a crash keeps the header, the navigation and the theme. Plugins signal the problem from their loaders; the app decides how it looks.
Answer 404 from a loader
When the record a URL asks for does not exist, throw notFound() from @tanstack/react-router in load:
import { notFound } from '@tanstack/react-router'
const loadNote = async (queryClient: QueryClient, idParam: string) => {
// ... read the note through the query cache, as in Add a route
if (!note) throw notFound()
return note
}The loader stops, the page renders the not-found screen, and the server answers with a 404 status, so search engines drop the URL. Add a route builds this page in full.
To show something other than the app's 404 page for one route, give its page module a notFound component:
const NoteNotFound = () => (
<p className="container mx-auto p-4 leading-relaxed">This note does not exist.</p>
)
export const route = defineRoute({
load: async ({ context, params }) =>
await loadNote(context.queryClient, params.id),
notFound: NoteNotFound,
})notFound loads with the page module, so it costs unvisited routes nothing. Without one, the route falls back to the router's defaultNotFoundComponent.
A URL that matches no route at all gets the same default 404 page. It still renders inside the main layout, because the main shell route matches every public URL.
Configure the app's error pages
The app sets both screens once, in getRouter in src/router.tsx. A new app already contains this:
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import {
ErrorActions,
NotFound,
RouteErrorPage,
} from '@vitnode/core/tanstack/layout'
export function getRouter() {
const router = createTanStackRouter({
// ...
defaultNotFoundComponent: () => <NotFound actions={<ErrorActions />} />,
defaultErrorComponent: ({ error }) => (
<RouteErrorPage actions={<ErrorActions />} error={error} />
),
})
return router
}| Component | Import | Shows |
|---|---|---|
NotFound | @vitnode/core/tanstack/layout | The localized 404 page |
RouteErrorPage | @vitnode/core/tanstack/layout | Error500Page, plus an overlay with the error's details in development only |
Error500Page | @vitnode/core/tanstack/layout | The localized 500 page alone, with the strings from core.global.errors.500 |
ErrorActions | @vitnode/core/tanstack/layout | A Go back button and a Back to home link |
Keep RouteErrorPage as the error component. Passing Error500Page directly works too, but you lose the development overlay that shows what went wrong.
Change the action buttons
actions takes any elements. To add a link next to the default buttons, render both:
import { buttonVariants } from '@vitnode/core/components/ui/button'
import { ErrorActions, NotFound } from '@vitnode/core/tanstack/layout'
import { HrefLink } from '@vitnode/core/tanstack/link'
// in createTanStackRouter({ ... })
defaultNotFoundComponent: () => (
<NotFound
actions={
<>
<ErrorActions />
<HrefLink className={buttonVariants({ size: 'lg', variant: 'outline' })} href="/site-notes/notes">
Browse notes
</HrefLink>
</>
}
/>
),Plugins throw notFound() or let errors surface from their loaders. The app
owns the screens, so every plugin's errors look the same.
Related
- Loading states: what the visitor sees while a page loads.
- Route module:
load,notFoundand the other module options.