We all try to debug and test our apps the best we can, but we can’t really prevent errors and exceptions from happening. Sometimes, something as simple as a HTTP request will fail – just because. But as always in life, what matters is how you handle problems that inevitably pop up 🧘♂️
Here’s an introduction to error and exception handling in your Vue apps.
The concepts of debugging, testing and error handling are often used together. If you’re starting out, the differences between them may not be clear enough. So we’ll start with defining them:
All of these things work together to develop reliable and stable apps. Today, we’re going to take a closer look at exception handling!
Are you still a bit unsure why exactly error handling is important, or why we might want to put more thought into it than just showing a generic error page?
In dev terms we want our app to fail gracefully, which just means in a controlled way. If you remember getting frustrated at an error message that just said „An unexpected error occurred“, or an app that crashed on you, you have experienced a not-so-graceful failing.
Handling specific errors and trying to solve them for your user is a great way to improve user experience. Failed to fetch new data? Try again, or offer the user the option to retry. Couldn’t save settings because of a network error? Tell the user so they can troubleshoot their connection.
Handling and logging errors correctly also improves your experience as a developer, because it usually makes it a lot easier to debug, and prevents losing data.
There are a few different options how to handle errors in your app and components. Depending on your project, you might use different techniques.
We’re introducing them going from a broad scope (handling errors globally for your whole app) to more specific (handling errors in components or distinct operations).
Your first step will probably always be to add global error handling with the Vue errorHandler
.
Add this function to your main application file (usually main.js
/ main.ts
), so it gets called whenever an error happens in a component, hook, watcher, event handlers etc. of your Vue app. This makes sure our app doesn’t crash, and gives us options to handle those exceptions instead.
You can add your custom logic to it, but at least we should log the error and display an error message.
import { createApp } from 'vue';
const app = createApp({});
// Global error handler
app.config.errorHandler = (err, instance, info) => {
// Handle the error globally
console.err("Global error:", error);
console.log("Vue instance:", instance);
console.log("Error info:", info);
// Add code for UI notifications, reporting or other error handling logic
};
app.mount("#app")’
The global error handler receives the JS error object, the Vue instance where the error happens and additional Vue-specific info about the error to help you pinpoint the problem.
To get more specific, we can use the errorCaptured
(options API) option or onErrorCaptured()
(composition API) lifecycle hook that gets called when an error occurs within a component’s scope or its child components. This includes errors during component rendering or computed properties.
Catching errors at this level gives you more control over the error handling of specific scenarios. You can for example use a component-specific UI for fallbacks and error messages. It also lets you implement specific error tracking to better analyze problems with that component.
<template>
<div>
<button @click="throwError">Throw Error</button>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref, onErrorCaptured } from 'vue';
const message = ref('');
const throwError = () => {
throw new Error('Simulated error');
};
onErrorCaptured((err) => {
message.value = 'An error occurred: ' + err.message;
return false;
});
</script>
Preventing the error from bubbling up with return false
will make sure that your global error handling does not react to the error, which it would due to error propagation rules.
To specifically handle routing errors, we can use the router.onError
method by Vue Router.
// Handle router errors
router.onError((err) => {
// Handle the router error here
console.error("Router error:", err);
// Add code for reporting or other error handling logic
});
If a part of your app contains potentially error-prone code, you can wrap it in a try/catch block to handle any problems immediately. Synchronous operations like API calls are great candidates for that!
fetchData() {
try {
// Add functions that may fail here
// If an error occurs, it will be caught:
} catch (err) {
// Handle the error here
console.error("Error:", err);
}
}
Now that you have everything you need to get started with Vue error handling, we’re going to end this guide with some resources that will improve your dev experience.
The Vue DevTools extension is mainly a tool to enhance your debugging experience, which is of course connected to your error handling logic. It’s an indispensable extension for every Vue dev.
Here are the feature highlights for error handling:
Logs are nice and all, but if you want to stay on top of all the errors that your users might be experiencing, a monitoring service will help you a lot. It also makes the experience of debugging less frustrating.
Error monitoring tools can notify you if your users experience issues, and provide a dashboard UI to learn more information about the problem. They show you which users are impacted, and display stack traces & breadcrumbs of events.
We’re running a SaaS product and have set up such a monitoring workflow for it. It brings us peace of mind and helps us make sure our users have a good experience. We already mentioned that we personally use Sentry for error and performance monitoring – here's a walkthrough of a debugging session where we use it. Disclaimer: They are also sponsoring MadeWithVueJS, but we have been using their service forever and would recommend it regardless 💚
There are of course many more tools to choose from, but they all more less work the same: They capture any errors, uncaught exceptions and other types of problems across your stack automatically once you add and configured their SDKs. Sentry reports errors from our Vue frontend and Laravel backend, for example.
You can also capture errors manually by passing your error objects and adding metadata like context, users, transaction names, release versions, attachments or tags. Here’s how that would look like for Sentry, so you get the idea:
import * as Sentry from "@sentry/vue";
try {
// Add functions that may fail here
} catch (err) {
Sentry.setUser({ email: "john.doe@example.com" });
Sentry.setTag("page_locale", "de-at");
Sentry.captureException(err);
// Handle the error here
}
There’s an open Vue School course about Application Monitoring in Vue.js with Sentry that you can watch to learn more.
We’ve now explored some different error handling strategies and learned about how exception handling ties together with debugging and testing to create sturdy Vue apps.
Now go forth and build some quality experiences with your (and our) favorite framework!