Understanding Lifecycle Hooks in Vue.js
If you’re building Vue applications and wondering when to fetch data, clean up resources, or interact with the DOM, you need to understand lifecycle hooks. These hooks are the backbone of component behavior in Vue 3, yet many developers struggle with knowing which hook to use and when.
This article explains Vue 3’s lifecycle hooks using the Composition API, covering each phase from component creation to destruction with practical examples you can apply immediately.
Key Takeaways
- The setup hook replaces both beforeCreate and created from Vue 2’s Options API
- onMounted is ideal for DOM operations and API calls after component rendering
- Always clean up resources in onUnmounted to prevent memory leaks
- Lifecycle hooks must be called synchronously inside the setup function
The Setup Hook: Your Component’s Entry Point
The setup hook is the cornerstone of the Vue 3 Composition API, replacing both beforeCreate and created from Vue 2. It runs before the component instance is fully initialized, making it the perfect place to define reactive state and compose your component logic.
import { ref, reactive } from 'vue'
export default {
setup(props, context) {
const count = ref(0)
const user = reactive({ name: '', email: '' })
// Logic runs immediately, before template compilation
console.log('Component initializing')
return { count, user }
}
}
Unlike Vue 2’s Options API, the setup function receives props and context as parameters, giving you direct access to component properties without using this.
Mounting Phase: onBeforeMount and onMounted
onBeforeMount() - Pre-DOM Preparation
The onBeforeMount hook fires after setup but before the component’s DOM is created. Use it for operations that need to happen before rendering but don’t require DOM access.
import { onBeforeMount } from 'vue'
export default {
setup() {
onBeforeMount(() => {
// Initialize non-DOM dependent resources
console.log('Template compiled, DOM not yet created')
})
}
}
onMounted() - DOM Ready Operations
onMounted is where most DOM-related initialization happens. The component’s template has been rendered and inserted into the document, making it safe to access elements and initialize third-party libraries.
import { ref, onMounted } from 'vue'
export default {
setup() {
const chartContainer = ref(null)
onMounted(async () => {
// Fetch data after component mounts
const data = await fetch('/api/data').then(r => r.json())
// Initialize chart library with DOM element
// Assuming Chart is imported from a charting library
new Chart(chartContainer.value, { data })
})
return { chartContainer }
}
}
Discover how at OpenReplay.com.
Updating Phase: Reactive State Changes
onBeforeUpdate() - Pre-render Logic
When reactive data changes trigger a re-render, onBeforeUpdate runs first. It’s your last chance to read the current DOM state before Vue updates it.
import { onBeforeUpdate } from 'vue'
export default {
setup() {
onBeforeUpdate(() => {
// Capture current scroll position before DOM updates
const scrollY = window.scrollY
// Store or use scrollY as needed
})
}
}
onUpdated() - Post-render Operations
After Vue updates the DOM, onUpdated fires. Be careful here—modifying reactive state can cause infinite loops.
import { onUpdated } from 'vue'
export default {
setup() {
onUpdated(() => {
// DOM has been updated
// Use nextTick() for specific state change reactions
console.log('Component re-rendered')
})
}
}
Unmounting Phase: Component Cleanup
onBeforeUnmount() - Pre-cleanup Tasks
Before a component is removed, onBeforeUnmount gives you access to the fully functional component one last time.
import { onBeforeUnmount, reactive } from 'vue'
export default {
setup() {
const state = reactive({ data: 'important info' })
onBeforeUnmount(() => {
// Save component state or notify external systems
localStorage.setItem('lastState', JSON.stringify(state))
})
return { state }
}
}
onUnmounted() - Resource Cleanup
onUnmounted is crucial for preventing memory leaks. Clean up event listeners, timers, and subscriptions here.
import { onMounted, onUnmounted } from 'vue'
export default {
setup() {
let timer
const handleResize = () => {
console.log('Window resized')
}
onMounted(() => {
timer = setInterval(() => {
console.log('Polling...')
}, 1000)
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
clearInterval(timer)
window.removeEventListener('resize', handleResize)
})
}
}
Practical Patterns and Best Practices
Remember these key points when working with Vue 3 lifecycle methods:
- Always import hooks from ‘vue’ before using them
- Call hooks synchronously inside setup()—not in callbacks or promises
- Vue 2 to Vue 3 migration:
beforeDestroyis nowonBeforeUnmount,destroyedisonUnmounted - Server-side rendering: Mounting and updating hooks don’t run during SSR
For testing components with lifecycle hooks, use Vue Test Utils to properly trigger and assert hook behavior.
Conclusion
Vue 3’s Composition API lifecycle hooks provide precise control over your component’s behavior at every stage. By understanding when each hook fires and what resources are available, you can write cleaner, more performant components. Start with setup() for initialization, use onMounted() for DOM operations, and always clean up in onUnmounted() to prevent memory leaks.
FAQs
Yes, you can call the same lifecycle hook multiple times in a single setup function. Vue will execute them in the order they were registered. This is useful for organizing code by feature rather than lifecycle phase.
onMounted runs once after the component is first rendered to the DOM. nextTick waits for the next DOM update cycle and can be used anywhere in your code, not just in lifecycle hooks. Use nextTick when you need to wait for a specific reactive change to be reflected in the DOM.
Yes, lifecycle hooks work perfectly with script setup. Simply import and call them directly without wrapping in a setup function. The script setup syntax automatically handles the setup context for you.
Wrap your lifecycle hook logic in try-catch blocks for error handling. Vue 3 also provides the onErrorCaptured hook to catch errors from child components. For global error handling, use app.config.errorHandler when creating your Vue application.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.