Vue cookbook · Intermediate

Catch component errors with an error boundary

One failing widget crashes the whole page with a blank screen.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Build an ErrorBoundary component with onErrorCaptured that shows a fallback for its subtree and stops the error from propagating, and report all errors centrally with app.config.errorHandler.

Solution

<!-- ErrorBoundary.vue -->
<script setup lang="ts">
import { onErrorCaptured, ref } from "vue";

defineProps<{ fallback?: string }>();
const error = ref<Error | null>(null);

onErrorCaptured((err) => {
  error.value = err instanceof Error ? err : new Error(String(err));
  return false;                                          // stop propagation: the rest of the page keeps working
});
</script>

<template>
  <div v-if="error" role="alert">
    <p>{{ fallback ?? "This section couldn't be loaded." }}</p>
    <button @click="error = null">Try again</button>
  </div>
  <slot v-else />
</template>
// main.ts
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);
app.config.errorHandler = (err, instance, info) => {
  // send to Sentry or your error tracker
  console.error("Vue error", info, err);
};
app.mount("#app");

Pitfalls

  • Swallowing errors in boundaries without reporting them.
  • Expecting onErrorCaptured to catch errors in event handlers' async code that isn't awaited by Vue; handle those in the handler or with window.onunhandledrejection.
  • One boundary around the whole app, which still blanks everything.

Variations

  • In Nuxt, <NuxtErrorBoundary> provides the same behaviour with an #error slot.
  • Combine with Suspense to show loading and error states for async setup.

Further reading: onErrorCaptured

Reference: https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured

More Vue recipes

Was this page helpful?