Chapter 101: Vue ‘beforeUnmount’ Lifecycle Hook
BeforeUnmount
(or its modern Composition API name onBeforeUnmount)
This hook is the last moment Vue gives you to clean up everything that your component created or subscribed to before it gets completely destroyed and removed from the DOM.
If you forget to clean up here, bad things happen:
- Timers keep running forever (setInterval / setTimeout)
- Event listeners stay attached to window / document → memory leaks
- Resize / scroll / mutation observers keep firing
- WebSocket / fetch / AbortController connections stay open
- Third-party libraries (Chart.js, Mapbox, Swiper…) keep references → memory never freed
- Your app slowly eats more RAM → users complain about lag / crash on long sessions
beforeUnmount / onBeforeUnmount is your last chance to say goodbye cleanly.
1. Exact position in the lifecycle (Vue 3 timeline)
Here is the full order with modern names first (Options API names in brackets):
|
0 1 2 3 4 5 6 7 8 9 10 11 |
onBeforeMount (beforeMount) ── about to insert into DOM onMounted (mounted) ── DOM is live onBeforeUpdate (beforeUpdate) ── about to re-render onUpdated (updated) ── DOM patched onBeforeUnmount (beforeUnmount) ── ← this one – component is about to be removed from DOM onUnmounted (unmounted) ── component is gone, instance is dead |
beforeUnmount / onBeforeUnmount runs just before Vue removes the component from the DOM and destroys the instance.
It is the last hook where you can still access:
- this / the component instance
- refs
- DOM ($el, ref.value)
- reactive state (ref.value, reactive objects)
After this hook finishes → onUnmounted runs → everything is gone.
2. What is available / NOT available in onBeforeUnmount?
| Feature / Access | Available in onBeforeUnmount()? | Reason / Explanation |
|---|---|---|
| this / component instance | Yes | Instance still exists |
| data() / ref() / reactive() | Yes | Reactive state is still alive |
| props | Yes | Props still present |
| computed | Yes | Computed still computed |
| Template refs (ref=”…”) | Yes | Refs still point to DOM nodes (they will be null soon) |
| Real DOM / $el | Yes | DOM still exists (but about to be removed) |
| DOM APIs (focus, scroll, measure) | Yes | You can still read current DOM state |
| Event listeners attached? | Yes | You must remove them here |
| Timers / intervals? | Yes | You must clear them here |
| The component is still visible? | Usually yes | Removal happens right after this hook |
Summary sentence to remember forever:
In beforeUnmount / onBeforeUnmount, everything is still alive and accessible — this is your last safe moment to clean up timers, listeners, subscriptions, third-party instances, and anything else that would leak if left behind.
3. Real, Practical Examples – Cleanup Patterns You Will Use Every Week
Example 1 – Clear interval / timeout (most common memory leak)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<script setup lang="ts"> import { ref, onMounted, onBeforeUnmount } from 'vue' const seconds = ref(0) let timer: ReturnType<typeof setInterval> | null = null onMounted(() => { timer = setInterval(() => { seconds.value++ }, 1000) }) onBeforeUnmount(() => { console.log('Cleaning up timer before unmount') if (timer) { clearInterval(timer) timer = null } }) </script> <template> <div>Seconds elapsed: {{ seconds }}</div> </template> |
→ Without onBeforeUnmount → timer keeps running forever → memory leak + unnecessary CPU usage
Example 2 – Remove event listener (window / document / custom element)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<script setup lang="ts"> import { onMounted, onBeforeUnmount } from 'vue' function handleResize() { console.log('Window resized:', window.innerWidth) } onMounted(() => { window.addEventListener('resize', handleResize) }) onBeforeUnmount(() => { console.log('Removing resize listener') window.removeEventListener('resize', handleResize) }) </script> |
→ Without cleanup → listener stays attached even after component is destroyed → memory leak + console spam
Example 3 – Abort ongoing fetch / API request
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<script setup lang="ts"> import { ref, onBeforeUnmount } from 'vue' const data = ref(null) const controller = new AbortController() async function fetchData() { try { const res = await fetch('/api/slow-endpoint', { signal: controller.signal }) data.value = await res.json() } catch (err: any) { if (err.name === 'AbortError') { console.log('Fetch aborted – component unmounted') } } } fetchData() onBeforeUnmount(() => { console.log('Aborting fetch before unmount') controller.abort() }) </script> |
→ Prevents “fetch after unmount” errors + cleans up network requests
Example 4 – Destroy third-party library instance
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<script setup lang="ts"> import { ref, onMounted, onBeforeUnmount } from 'vue' import Chart from 'chart.js/auto' const canvas = ref<HTMLCanvasElement | null>(null) let chart: Chart | null = null onMounted(() => { if (canvas.value) { chart = new Chart(canvas.value, { /* config */ }) } }) onBeforeUnmount(() => { if (chart) { chart.destroy() chart = null console.log('Chart instance destroyed') } }) </script> <template> <canvas ref="canvas" width="600" height="400"></canvas> </template> |
→ Chart.js keeps canvas references & animation loops → must call .destroy()
4. Quick Summary Table – beforeUnmount / onBeforeUnmount in 2026
| Question | Answer / Reality in 2026 |
|---|---|
| When does it run? | Just before component is removed from DOM & destroyed |
| Is data/props/computed available? | Yes |
| Is DOM / $el / refs available? | Yes (last chance to read / cleanup DOM) |
| Is this available? | Yes (Options API) |
| Do modern developers use it? | Very often – cleanup timers, listeners, libs |
| Modern replacement | onBeforeUnmount(() => { … }) |
| Still asked in interviews? | Yes — “Where do you clean up event listeners?” |
Pro Tips from Real Projects (Hyderabad 2026)
- Most important cleanup hook → onBeforeUnmount (timers, listeners, abort, destroy)
- Always cleanup — every addEventListener must have removeEventListener, every setInterval must have clearInterval, every third-party .init() must have .destroy()
- Use AbortController for fetch / async operations — cleanest way to cancel
- In SSR / Nuxt → onBeforeUnmount runs only client-side
- Never rely on onUnmounted for cleanup — it runs after DOM removal → some DOM APIs may already be gone
- Combine onMounted + onBeforeUnmount as a pair — init & cleanup always go together
Your mini homework:
- Create a component with a timer (setInterval) that updates a counter
- Add onBeforeUnmount to clearInterval
- Toggle the component with v-if → watch console logs & CPU usage
- Remove cleanup → toggle many times → see timer keeps running (memory leak)
Any part confusing? Want full examples for:
- Cleanup patterns (timers, event listeners, AbortController, ResizeObserver)?
- onBeforeUnmount + third-party lib (Chart.js / Mapbox / Swiper) destroy?
- beforeUnmount vs unmounted difference in practice?
- Lifecycle in <KeepAlive> / <Transition> / <Suspense>?
Just tell me — we’ll trace the lifecycle and build real cleanup examples together step by step 🚀
