JAVASCRIPT
Robust Error Boundary Composable for Vue 3
Implement a robust error boundary in Vue 3 using the Composition API to gracefully catch and display errors from child components, preventing app crashes.
// composables/useErrorBoundary.js
import { ref, onErrorCaptured } from 'vue';
export function useErrorBoundary() {
const error = ref(null);
onErrorCaptured((err, instance, info) => {
error.value = err;
console.error('Error captured:', err, instance, info);
// Return false to prevent the error from propagating further up the component tree
// or true to let it continue propagating.
return true;
});
const resetError = () => {
error.value = null;
};
return { error, resetError };
}
// components/ErrorFallback.vue
<template>
<div v-if="error" class="error-fallback">
<h3>Oops! Something went wrong.</h3>
<p>{{ error.message }}</p>
<button @click="$emit('reset')">Try again</button>
</div>
<slot v-else></slot>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
defineProps({
error: Error,
});
defineEmits(['reset']);
</script>
<style scoped>
.error-fallback {
border: 1px solid #ff0000;
padding: 15px;
background-color: #ffe0e0;
color: #c00;
border-radius: 8px;
margin-top: 20px;
}
</style>
// src/App.vue (or any parent component)
<template>
<ErrorBoundary v-slot="{ hasError, resetError }">
<ErrorFallback :error="hasError" @reset="resetError">
<TheContentThatMightFail />
</ErrorFallback>
</ErrorBoundary>
</template>
<script setup>
import { defineComponent, h } from 'vue';
import { useErrorBoundary } from './composables/useErrorBoundary';
import ErrorFallback from './components/ErrorFallback.vue';
const ErrorBoundary = defineComponent({
setup(_, { slots }) {
const { error, resetError } = useErrorBoundary();
return () => slots.default({
hasError: error.value,
resetError,
});
},
});
// A component designed to potentially throw an error
const TheContentThatMightFail = defineComponent({
setup() {
const throwError = () => {
throw new Error('This is a simulated component error!');
};
return () => h('div', [
h('p', 'Content that might fail.'),
h('button', { onClick: throwError }, 'Trigger Error')
]);
},
});
</script>
How it works: This example provides a robust error boundary solution for Vue 3 using a composable (`useErrorBoundary`) and a renderless component pattern. The `onErrorCaptured` lifecycle hook within `useErrorBoundary` catches errors from descendant components, storing them in a reactive `error` ref. The `ErrorBoundary` component then uses a slot to expose `hasError` and `resetError` to its parent, allowing an `ErrorFallback` component to display the error gracefully or render the original content. This prevents application crashes and improves user experience.