JAVASCRIPT

Vue 3 Global Error Handling with `app.config.errorHandler`

Implement a centralized error handling mechanism in your Vue 3 application to gracefully catch and report unhandled errors from components, ensuring a more robust user experience.

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// Global error handler
app.config.errorHandler = (err, instance, info) => {
  // `err` is the error object
  // `instance` is the component instance where the error originated
  // `info` is a Vue-specific error info string (e.g., "render function", "mounted hook")

  console.error('Caught an error globally:');
  console.error('Error:', err);
  console.error('Component instance:', instance);
  console.error('Error info (Vue lifecycle hook/source):', info);

  // --- You would typically send this error to an error reporting service ---
  // Example: Sentry, LogRocket, custom API endpoint
  // reportErrorToService({
  //   message: err.message,
  //   stack: err.stack,
  //   component: instance?.$.type?.name || 'Unknown',
  //   info: info,
  //   timestamp: new Date().toISOString()
  // });

  // Optional: Display a user-friendly error message or fallback UI
  // For production, you might want to replace the app with a generic error page
  // document.body.innerHTML = '<h1>An unexpected error occurred. Please refresh.</h1>';
};

// Optional: Global warning handler (only in development)
// app.config.warnHandler = (msg, instance, trace) => {
//   console.warn('Caught a warning globally:');
//   console.warn('Message:', msg);
//   console.warn('Component instance:', instance);
//   console.warn('Trace:', trace);
// };

app.mount('#app');


// src/components/ErrorProneComponent.vue
<template>
  <div class="error-prone-component">
    <h3>Error Prone Component</h3>
    <button @click="triggerErrorInMethod">Trigger Error in Method</button>
    <button @click="triggerErrorInLifecycle">Trigger Error in Mounted Hook (check console)</button>
    <button @click="triggerErrorInWatcher">Trigger Error in Watcher (check console)</button>
    <button @click="asyncError">Trigger Async Error (won't be caught by Vue's handler directly)</button>

    <p v-if="localError">{{ localError }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';

const localError = ref('');
const count = ref(0);

// Error in a method
const triggerErrorInMethod = () => {
  try {
    throw new Error('This is an error from a component method!');
  } catch (e) {
    // Catching locally to show it doesn't propagate to global handler if caught
    // To test global handler, remove the try-catch block here.
    console.log('Caught locally:', e.message);
    localError.value = e.message;
  }
};

// Error in a lifecycle hook (will be caught by global handler)
onMounted(() => {
  // Uncomment the line below to test global error handler
  // throw new Error('Error from mounted hook!');
  console.log('Mounted hook executed.');
});

// Error in a watcher (will be caught by global handler)
watch(count, () => {
  // Uncomment the line below to test global error handler
  // throw new Error('Error from watcher!');
  console.log('Count changed:', count.value);
});

const triggerErrorInLifecycle = () => {
  console.log('Attempting to trigger error in mounted (if uncommented in setup)');
  // To reliably trigger an error in a mounted hook *after* mount,
  // you'd typically have to re-mount the component or trigger a state
  // change that leads to a render error. For demonstration, the direct
  // `throw new Error` in onMounted is more direct.
  // Let's increment count to trigger watcher error if uncommented
  count.value++;
};

const triggerErrorInWatcher = () => {
  console.log('Attempting to trigger error in watcher (if uncommented in setup)');
  count.value++; // This will trigger the watcher
};

// Async error (e.g., Promise rejection) is not caught by Vue's error handler
// You need a separate global unhandledrejection listener for these
const asyncError = () => {
  Promise.reject('This is an unhandled promise rejection!');
};

// Global unhandled promise rejection handler (in main.js or separate util)
/*
window.addEventListener('unhandledrejection', (event) => {
  console.error('Caught an unhandled promise rejection globally:');
  console.error('Reason:', event.reason);
  // reportErrorToService({
  //   message: event.reason?.message || 'Unhandled Promise Rejection',
  //   stack: event.reason?.stack,
  //   type: 'Promise Rejection',
  //   timestamp: new Date().toISOString()
  // });
  event.preventDefault(); // Prevent default browser handling if desired
});
*/
</script>

<style scoped>
.error-prone-component {
  border: 1px solid #ff6666;
  padding: 20px;
  margin-top: 20px;
  background-color: #ffeeee;
  border-radius: 8px;
}
button {
  margin: 5px;
  padding: 8px 12px;
  cursor: pointer;
}
p {
  color: red;
  font-weight: bold;
}
</style>

// src/App.vue
/*
<template>
  <div id="app">
    <h1>Vue 3 Error Handling Demo</h1>
    <ErrorProneComponent />
  </div>
</template>

<script setup>
import ErrorProneComponent from './components/ErrorProneComponent.vue';
</script>
*/
How it works: This snippet demonstrates how to set up a global error handler in a Vue 3 application using `app.config.errorHandler` in `main.js`. This handler catches unhandled errors that occur within component lifecycles, watchers, and render functions, preventing them from crashing the entire application. It logs the error, the component instance, and specific Vue error information, providing a central point for debugging and integration with error reporting services like Sentry. It also notes that asynchronous errors (e.g., unhandled promise rejections) require a separate `window.unhandledrejection` listener.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs