Not Found
π¨βπΌ Right now if you go to a route that doesn't exist (like
/does/not/exist
),
our root route's error boundary will handle that. Unfortunately, this error
boundary is pretty limited because we can't rely on the root route's loader for
any data. This makes our 404 page pretty useless.To be clear, we do have a 404 page for notes and users that aren't found. So
going to
/users/does-not-exist
works well. And also
/users/kody/notes/does-not-exist
works well too. We just want to do the same
for routes that don't exist at all. Like /does-not-exist
or
/users/kody/does-not-exist
.So we're going to use a router feature called a "splat route" to handle this.
A splat route effectively says "match any route that starts with this path". So
we can create a splat route at the end of our router that matches any route that
starts with
/
and because splat routes receive a lower priority than others,
we'll know if the splat route gets rendered it's because no other route matched
so we can show our 404 error.It's important to note that splat routes aren't only useful for 404s, but they
can be really handy for use with a CMS where the URL segments are dynamic.
As we're following the
remix-flat-routes
convention, to create a route that
matches /*
, π¨ we'll create a file at .With that file created, you need to π¨ create a loader that throws a
404
response:export async function loader() {
throw new Response('Not found', { status: 404 })
}
Next, let's π¨ export the
ErrorBoundary
:import { Link, useLocation } from '@remix-run/react'
import { GeneralErrorBoundary } from '../components/error-boundary.tsx'
// ...
export function ErrorBoundary() {
const location = useLocation()
return (
<GeneralErrorBoundary
statusHandlers={{
404: () => (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-3">
<h1>We can't find this page:</h1>
<pre className="text-body-lg whitespace-pre-wrap break-all">
{location.pathname}
</pre>
</div>
<Link to="/" className="text-body-md underline">
Back to home
</Link>
</div>
),
}}
/>
)
}
Finally, we'll want to π¨ export a default export just in case the loader is
changed or fails in some way, we'll still be able to display some error to the
user:
// ...
export default function NotFound() {
// due to the loader, this component will never be rendered, but we'll return
// the error boundary just in case.
return <ErrorBoundary />
}
// ...
And with that, you should now be able to go to any route that doesn't exist and
you should still see the header and footer. This will make it a much better user
experience when folks hit a page that doesn't exist.