404 pages with Vue.js and vue-router
In this post we will explore how to render or redirect to 404 Not found page using Vue.js.
Rendering
To render 404 page we need to update our router definition. It can be found in src/router/router.ts.
Example routes table:
const routes: RouteRecordRaw[] = [
{
path: '/',
component: HomePage,
name: 'home_page',
},
{
path: '/about/',
component: AboutPage,
name: 'about_page',
},
]
To render a 404 page we need to add entry which will catch everything and render 404 page.
{ path: '/:catchAll(.*)*', component: Error404Page, },
Let’s break down what path '/:catchAll(.*)*' is doing:
:catchAll- is the name of the path’s parameter, the name can be anything. In most cases you will find it named:pathMatchwhich can be confusing as it sounds a bit as some reserved word or function name,(.*)- is a regex of what we want to capture, in this case we we want to catch everything,*- this is very important as it tellsvue-routerto catch paths with multiple segments e.g./foo/bar/and/foo/bar/baz, without it it would only match single segment paths e.g./fooor/bar. Read more.
Warning! When we do a soft 404 page it won’t actually result in 404 HTTP response code to the client. This will impact SEO as the browser will see
200 OKresponse status. Which indicates that page actually exists and could be indexed.
Redirecting
We can also redirect user to 404 page like this:
{
path: '/404.html',
component: ErrorNotFound,
name: 'not_found_page',
},
{ path: '/:pathMatch(.*)*', redirect: () => (window.location.href = '/404.html') },
This redirect and window.location.href is crucial because it initiates a new browser request for /404.html. This allows your server (e.g., Load Balancer/CDN, as detailed in my article about hosting SPA applications on GCP
) to intercept the non-existent file request and return the necessary 404 HTTP status code to the client.
Going deeper
Question: what will happen if we put our catchAll entry at the begging of the route’s list? Think about it for a moment and read on.
I’ve read that those routes are evaluated in order as defined, this could make sense, as it is simple and clear but while testing it didn’t work at all. Turns out it’s not as simple and entries are matched by their weight (or rank), from less to more complex ones. This ensures that a specific route like /about is always matched before the generic catch-all route, regardless of definition order. Current weights can be found in vue-router’s code.
Thanks for reading :)