Error handling in NuxtJS
There are a lot of ways to handle errors in NuxtJS and they are all documented. Despite that, it's sometimes still difficult to determine what options are available in a specific scenario. This post is my attempt at creating an overview to use as a reference in the future.
Handling Errors
Error handling works differently in SSR (server-side rendering) and SPA (single page application) mode. Since the pages rehydrate as SPAs even in SSR mode, you always need to implement the SPA error handling. But you only need the SSR error handling if you're going to serve the application in SSR mode.
The three main sources of unhandled errors are the following:
- asynchronous data loading, (e.g. in the
asyncData
method) - template rendering
- event handlers
In SSR mode, only the first two types of errors can happen. If they aren't handled, the application will fail and the static server error page will be shown:
You won't see it in development mode because the Youch error page will be shown instead:
You need to build the application in production mode and serve it to see the actual static error page:
npm run build
npm run start
You can log the unhandled errors in server error middleware but you can't handle them there to prevent the static server error page from being shown. The server error middleware is defined as a hook in nuxt.config.js
:
export default {
// ...
hooks: {
render: {
errorMiddleware(app) {
app.use((error, _req, _res, next) => {
if (error) {
console.log("Logged in errorMiddleware", error);
}
next(error);
});
},
},
},
};
To avoid this error page in your application, you need to handle the errors beforehand. For the asyncData
method, this means that you must handle the asynchronous promise rejection in code. You can still call the Context error
method from code:
import { Vue, Component } from "vue-property-decorator";
import { Context } from "@nuxt/types";
@Component({
asyncData: (ctx: Context) => {
// ...
ctx.error({ message: "Error fetching data..." });
},
})
export default class AsyncErrorPage extends Vue {
// ...
}
This will show the NuxtJS error page that's served by the NuxtJS application:
Template rendering errors can be handled in the errorCaptured
lifecycle hook of any Vue.js component. The best central place to implement this hook in a NuxtJS project is in a layout (either the default one or a custom one):
import { Vue, Component } from "vue-property-decorator";
@Component({
errorCaptured(this: SafeLayout, err: Error, vm: Vue, info: string) {
this.renderError = info === "render"; // indicates a template rendering error
console.log("error handled in layout", err, vm, info);
return false; // stops the error from propagating further
},
})
export default class SafeLayout extends Vue {
renderError = false;
}
The hook will handle any rendering errors in pages that use this layout and stop the static error page from showing in SSR mode. It will also handle other types of unhandled errors. Because of that, I'm setting a flag when it's a render error. Only in this case, the page wasn't rendered (correctly). Therefore, I want to render an alternative page notifying the user about the error:
<template>
<div>
<Nuxt v-if="!renderError" />
<div v-if="renderError">Render error occured</div>
</div>
</template>
The same lifecycle hook will also handle client-side rendering errors and unhandled synchronous errors in event handlers. However, if these errors aren't handled in the errorCaptured
lifecycle hook, they won't result in the static error page:
- Unhandled client-side rendering errors will show the NuxtJS error page.
- Unhandled synchronous errors in event handlers will be quietly logged by the browser in the console.
Both types of errors can be intercepted and logged in the Vue.js global error handler but they can't be stopped from propagating to prevent the error page from being shown:
import Vue from "vue";
Vue.config.errorHandler = (err: Error, vm: Vue, info: string) => {
console.log("Logged in Vue global error handler", err, vm, info);
};
Unhandled asynchronous errors can be intercepted and logged in the onunhandledrejection
browser event but also can't be stopped from further propagation:
window.onunhandledrejection = (event: PromiseRejectionEvent) => {
console.log("Logged in window.onunhandledrejection", event);
};
If they originate from the asyncData
methods, the NuxtJS error page will be shown. If they happened in an event handler, they will be quietly logged by the browser console.
In a NuxtJs application, both the Vue.js global error handler and the onunhandledrejection
event handler must be defined in a client-side plugin in a file placed in the plugins
folder. This file is then registered in the nuxt.config.js
file:
export default {
// ...
plugins: [{ src: "plugins/globalErrorHandler.ts", mode: "client" }],
};
All of the options described above are shown in the following two diagrams:
Errors in event handlers can only happen on the client:
Other types of errors can happen on the client or the server:
Customizing Error Pages
Even if you intend to handle all errors in your application, there's always a risk that you'll miss something and still see one of the default error pages. Fortunately, you can customize both of them to make them more consistent with the rest of your application.
You can customize the NuxtJS error page as a component in layouts/error.vue
:
import { Vue, Component, Prop } from "vue-property-decorator";
import { NuxtError } from "@nuxt/types";
@Component
export default class ErrorLayout extends Vue {
@Prop({ type: Object, required: true }) readonly error!: NuxtError;
}
You can use the error details passed to it in the error
prop to provide more information to the user:
<template>
<div>Custom error page: {{ error.message }}</div>
</template>
You can also replace the default static error page with your own. But unlike the NuxtJS error page, it's not a part of the NuxtJS application and is served as a simple static HTML page. ALthough it can include CSS and JavaScript, it might still be difficult to make it appear as a regular part of your application. The HTML file must be named error.html
and placed in the app/views
folder (you also need to create the folder yourself).
An example NuxtJS project with all the described error types and methods to handle them is available in my GitHub repository.
In a universal NuxtJS application, many different types of errors need to be handled. It's a good idea to create a plan for that early in the project. Usually, you'll want to handle all the errors explicitly yourself and only fall back to the default error pages when you fail to do that. To improve the user experience, it still makes sense to customize those error pages. It's also a good idea to intercept unhandled errors and log them so that you're aware of them and can fix them.