Vue lifecycle hooks run at defined points as a component is created, mounted, updated, and unmounted. setup code establishes reactive state; onMounted is for DOM-dependent work; onUpdated observes completed patches sparingly; onUnmounted releases resources. Hooks describe timing, not an architecture for business logic.
Every Vue component goes through a series of initialization steps - creating reactive data, compiling the template, mounting to the DOM, updating when data changes, and unmounting. Lifecycle hooks let you run code at specific stages.
| Hook | When it runs | Common use |
|---|---|---|
| onBeforeMount | Before component is mounted to DOM | Rarely needed |
| onMounted | After component is mounted | Fetch data, access DOM, init libraries |
| onBeforeUpdate | Before DOM updates | Access pre-update DOM state |
| onUpdated | After DOM updates | Access updated DOM |
| onBeforeUnmount | Before component is destroyed | Cleanup (timers, listeners) |
| onUnmounted | After component is destroyed | Final cleanup |
| onErrorCaptured | When child throws error | Error boundaries |
<template>
<div>
<p>Users: {{ users.length }}</p>
<p>Timer: {{ seconds }}s</p>
<div ref="chartContainer"></div>
</div>
</template>
<script setup>
import {
ref, onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted,
onErrorCaptured
} from 'vue'
const users = ref([])
const seconds = ref(0)
const chartContainer = ref(null)
let timer = null
// onBeforeMount - component not yet in DOM
onBeforeMount(() => {
console.log('Before mount - DOM not ready yet')
})
// onMounted - component is in DOM, refs are available
onMounted(async () => {
console.log('Mounted - DOM is ready')
// 1. Fetch initial data
const res = await fetch('/api/users')
users.value = await res.json()
// 2. Access DOM element via ref
console.log('Chart container:', chartContainer.value)
// initChart(chartContainer.value) // init third-party library
// 3. Start timer
timer = setInterval(() => seconds.value++, 1000)
// 4. Add event listener
window.addEventListener('resize', handleResize)
})
// onBeforeUpdate - before DOM re-renders
onBeforeUpdate(() => {
console.log('Before update - old DOM still accessible')
})
// onUpdated - after DOM re-renders
onUpdated(() => {
console.log('Updated - DOM reflects new data')
// Scroll to bottom of list after update
// listEl.value.scrollTop = listEl.value.scrollHeight
})
// onBeforeUnmount - cleanup before destruction
onBeforeUnmount(() => {
console.log('Before unmount - cleanup time')
clearInterval(timer)
window.removeEventListener('resize', handleResize)
})
// onUnmounted - component is gone
onUnmounted(() => {
console.log('Unmounted - component destroyed')
})
// onErrorCaptured - catch errors from child components
onErrorCaptured((error, instance, info) => {
console.error('Child error:', error, info)
return false // prevent error from propagating
})
function handleResize() {
console.log('Window resized:', window.innerWidth)
}
</script>
Start subscriptions, observers, timers, or third-party widgets only when their required element and owner exist, then stop them during unmount. If a watcher creates asynchronous work, use watcher cleanup as well; waiting until component unmount is too late when the dependency changes repeatedly.
Do not mutate reactive state unconditionally in onUpdated, because that can schedule another update and create a loop. Use a watcher for a specific dependency or nextTick when code must wait for one DOM patch. With KeepAlive, activated and deactivated hooks describe cached visibility without full mounting and unmounting.
Register Composition API lifecycle hooks synchronously during setup so Vue can associate them with the current component instance. setup itself runs before mounting. onBeforeMount runs before the first DOM patch, and onMounted runs after the component DOM and synchronous child components are mounted.
For reactive updates, onBeforeUpdate runs after state changes but before Vue patches the DOM; onUpdated runs after that patch. Multiple state changes can be batched into one render, so updated is not a one-to-one change event. Before removal, onBeforeUnmount runs while the instance is still active; onUnmounted runs after its DOM, effects, and synchronous children are stopped.
Use onMounted for browser-only work that needs rendered DOM, such as observers, measurements, or third-party widgets. Remove every listener, observer, timer, subscription, and in-flight request in onUnmounted or through the cleanup mechanism of the watcher that created it.
Do not mutate component state unconditionally inside onUpdated; that schedules another render and can create an update loop. Watch the specific reactive source instead. Use nextTick when code needs the DOM after Vue has applied a known state change rather than using a broad updated hook.
import { onMounted, onUnmounted, ref } from 'vue'
const width = ref(0)
const measure = () => { width.value = window.innerWidth }
onMounted(() => {
measure()
window.addEventListener('resize', measure)
})
onUnmounted(() => {
window.removeEventListener('resize', measure)
})
The same function reference is used for registration and cleanup, so remounting the component does not accumulate listeners.
onErrorCaptured observes errors from descendant renders, event handlers, watchers, setup, and lifecycle hooks. Report useful component context and decide deliberately whether returning false should stop propagation. Keep an application-level error handler because a local boundary may itself fail or may not cover every source.
onRenderTracked and onRenderTriggered are development debugging hooks that reveal which reactive dependency was tracked or triggered a render. They are not production analytics APIs. onActivated and onDeactivated apply to components cached by KeepAlive, which can enter and leave the DOM many times before final unmount.
<script setup>
const topic = 'Vue Lifecycle Hooks onMounted, onUnmounted';
</script>
<template>
<section>
<h2>{{ topic }}</h2>
<p>Practice the concept with data, events, and a boundary case.</p>
</section>
</template>
Explore 500+ free tutorials across 20+ languages and frameworks.