Error Handling in Vue
Error Handling Strategies
Vue provides several mechanisms to handle errors gracefully — from component-level error boundaries to global error handlers. Good error handling prevents blank screens and gives users meaningful feedback.
<!-- ErrorBoundary.vue — catches errors from child components -->
<template>
<div>
<div v-if="error" class="error-boundary">
<h3>Something went wrong</h3>
<p>{{ error.message }}</p>
<details v-if="isDev">
<summary>Error details</summary>
<pre>{{ errorInfo }}</pre>
</details>
<button @click="reset">Try Again</button>
</div>
<slot v-else />
</div>
</template>
<script setup>
import { ref, onErrorCaptured } from 'vue'
const error = ref(null)
const errorInfo = ref(null)
const isDev = import.meta.env.DEV
// Catches errors from ALL descendant components
onErrorCaptured((err, instance, info) => {
error.value = err
errorInfo.value = info
// Log to error tracking service
console.error('Caught error:', err, 'in:', info)
// logToSentry(err, { component: instance?.$options.name, info })
return false // prevent error from propagating further
})
function reset() {
error.value = null
errorInfo.value = null
}
</script>
<!-- Usage: -->
<!-- <ErrorBoundary> -->
<!-- <UserProfile :userId="userId" /> -->
<!-- </ErrorBoundary> -->
// main.js — global error handlers
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// Global error handler — catches all unhandled errors
app.config.errorHandler = (err, instance, info) => {
console.error('Global error:', err)
console.error('Component:', instance?.$options.name)
console.error('Info:', info)
// Send to error tracking (Sentry, Bugsnag, etc.)
// Sentry.captureException(err, { extra: { info } })
// Show user-friendly notification
// toast.error('An unexpected error occurred')
}
// Global warning handler (development only)
app.config.warnHandler = (msg, instance, trace) => {
console.warn('Vue warning:', msg)
console.warn('Trace:', trace)
}
// Handle unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason)
event.preventDefault() // prevent default browser error
})
// Handle global JS errors
window.addEventListener('error', (event) => {
console.error('Global JS error:', event.error)
})
app.mount('#app')
<!-- Async error handling patterns -->
<template>
<div>
<div v-if="state.loading">Loading...</div>
<div v-else-if="state.error" class="error">
<p>{{ state.error }}</p>
<button @click="fetchData">Retry</button>
</div>
<div v-else>
<pre>{{ state.data }}</pre>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue'
const state = reactive({
data: null,
loading: false,
error: null,
})
async function fetchData() {
state.loading = true
state.error = null
try {
const res = await fetch('/api/data')
if (!res.ok) {
// HTTP errors (4xx, 5xx) don't throw — check manually
throw new Error(`Server error: ${res.status} ${res.statusText}`)
}
state.data = await res.json()
} catch (err) {
if (err instanceof TypeError) {
// Network error (no internet, CORS, etc.)
state.error = 'Network error. Check your connection.'
} else if (err.name === 'AbortError') {
// Request was cancelled
state.error = null // not really an error
} else {
state.error = err.message || 'An unexpected error occurred'
}
console.error('Fetch error:', err)
} finally {
state.loading = false
}
}
onMounted(fetchData)
</script>
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.