Skip to content
← All questions
🔥 Advanced

How does error handling work in Vue?

🧩 Components🛡️ Error Handling⏳ Suspense

Vue provides onErrorCaptured to catch errors from descendant components, letting you build error boundaries similar to React's componentDidCatch. Combined with app.config.errorHandler for global errors and defineAsyncComponent's error options, you can handle failures at every level.

Flowchart showing Vue error propagation: child error bubbles through onErrorCaptured to app.config.errorHandler

onErrorCaptured (error boundary)

A parent component can catch errors thrown by any descendant (including async errors from lifecycle hooks and watchers):

vue
<!-- ErrorBoundary.vue -->
<script setup>
import { ref, onErrorCaptured } from 'vue'

const error = (ref < Error) | (null > null)

onErrorCaptured((err, instance, info) => {
  error.value = err
  console.error(`Error in ${info}:`, err)
  return false // stop propagation to parent error handlers
})
</script>

<template>
  <div v-if="error" class="error-state">
    <p>Something went wrong: {{ error.message }}</p>
    <button @click="error = null">Try again</button>
  </div>
  <slot v-else />
</template>
Open in Vue Playground
vue
<!-- Usage -->
<template>
  <ErrorBoundary>
    <DashboardWidget />
  </ErrorBoundary>
</template>
Open in Vue Playground

The callback receives three arguments:

ArgumentDescription
errThe error object
instanceThe component instance that threw
infoA string describing where the error was caught (e.g., "render", "setup", "watcher")

Returning false prevents the error from propagating further. Returning true (or nothing) lets it bubble up to parent error handlers and app.config.errorHandler.

Global error handler

Catches any error that wasn't stopped by onErrorCaptured:

ts
// main.ts
const app = createApp(App)

app.config.errorHandler = (err, instance, info) => {
  // Send to error tracking service
  errorTracker.captureException(err, { info })
}

Error flow

Component throws → nearest onErrorCaptured (can stop here)
                 → parent onErrorCaptured (can stop here)
                 → ... up the tree ...
                 → app.config.errorHandler
                 → console.error (if nothing catches it)

Async component errors

defineAsyncComponent has its own error handling with retry logic:

ts
const AsyncWidget = defineAsyncComponent({
  loader: () => import('./Widget.vue'),
  errorComponent: ErrorDisplay,
  timeout: 10000,
  onError(error, retry, fail, attempts) {
    if (attempts <= 3) {
      retry()
    } else {
      fail() // shows errorComponent
    }
  }
})

What Vue error handling catches

SourceCaught?
Render/template errorsYes
Lifecycle hook errorsYes
Watcher callback errorsYes
Component event handler errorsYes
Custom directive hook errorsYes
setTimeout/setInterval callbacksNo (not tracked by Vue)
Native event listeners added manuallyNo
Errors in third-party librariesNo (unless called from Vue lifecycle)

For errors outside Vue's tracking (timers, manual listeners), use window.addEventListener('error', handler) or window.addEventListener('unhandledrejection', handler).

Practical pattern: multiple error boundaries

Wrap independent sections so one failure doesn't take down the whole page:

vue
<template>
  <header>
    <ErrorBoundary>
      <Navigation />
    </ErrorBoundary>
  </header>

  <main>
    <ErrorBoundary>
      <RouterView />
    </ErrorBoundary>
  </main>

  <aside>
    <ErrorBoundary>
      <Sidebar />
    </ErrorBoundary>
  </aside>
</template>
Open in Vue Playground

See also: What are async components? · How does Suspense work?

References

Released under the MIT License.