Chapter 99: Vue ‘beforeUpdate’ Lifecycle Hook
BeforeUpdate
This hook is not one you reach for every day (unlike onMounted or onBeforeUnmount), but when you do need it, it solves a very specific problem that no other hook can solve cleanly.
Let me explain it step by step — like I’m sitting next to you showing you exactly when it runs, what is ready, what is not ready, and the rare but real situations where people still use it in 2026.
1. Exact position in the lifecycle (Vue 3 timeline)
Here is the full order with modern Composition API names first (Options API names in brackets):
|
0 1 2 3 4 5 6 7 8 9 10 11 |
onBeforeMount (beforeMount) ── template compiled, about to insert into DOM onMounted (mounted) ── DOM is live & inserted onBeforeUpdate (beforeUpdate) ── ← this one – reactive data changed, about to re-render / patch the DOM onUpdated (updated) ── DOM has been patched & updated onBeforeUnmount (beforeUnmount) ── about to be destroyed onUnmounted (unmounted) ── component is gone |
beforeUpdate / onBeforeUpdate runs every single time the component is going to re-render because something reactive changed (ref value, reactive property, prop, computed dependency, etc.).
It is the last moment you can run code before Vue starts diffing and patching the real DOM.
2. What is available / NOT available in onBeforeUpdate?
This is the key table — memorize when you can and cannot do certain things.
| Feature / Access | Available in onBeforeUpdate()? | Reason / Explanation |
|---|---|---|
| this / component instance | Yes | Fully populated |
| data() / ref() / reactive() | Yes | Reactive state is already updated (new values are set) |
| props | Yes | New prop values are already received |
| computed | Yes | All computed properties have been re-evaluated with new values |
| methods / functions | Yes | All functions available |
| Old vs new values | Partially | You can still access old values via watch or by storing them manually |
| Real DOM / $el | Yes | DOM exists (component is already mounted) |
| Template refs (ref=”…”) | Yes | Refs are populated |
| DOM APIs (focus, scroll, measure) | Yes | You can read current DOM state (but it reflects old values — before patch) |
| The DOM has been updated yet? | No | Patch / re-render has not happened yet — DOM still shows old state |
Summary sentence to remember forever:
In beforeUpdate / onBeforeUpdate, the new reactive values are already set, computed properties have been re-calculated, but the DOM still shows the old state — Vue is about to patch it.
This is the only hook that gives you a chance to:
- read the old DOM state before it gets overwritten
- compare old vs new values (if you stored them)
- run code before the expensive DOM diff & patch happens
3. Real, Practical Example – Measuring DOM Before & After Update
|
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<template> <div class="demo-box"> <h2>beforeUpdate Hook Demo</h2> <p>Count: {{ count }}</p> <button @click="count++">+1 (triggers re-render)</button> <div ref="contentBox" class="content-box"> <p v-for="n in count" :key="n"> Line {{ n }} — this box grows every time count increases </p> </div> <p>Current height (updated in onUpdated): {{ currentHeight }} px</p> <p>Height before patch (read in onBeforeUpdate): {{ heightBeforeUpdate }} px</p> </div> </template> <script setup lang="ts"> import { ref, onBeforeUpdate, onUpdated } from 'vue' const count = ref(5) const contentBox = ref<HTMLElement | null>(null) const heightBeforeUpdate = ref(0) const currentHeight = ref(0) // Read DOM height **before** Vue patches it onBeforeUpdate(() => { if (contentBox.value) { heightBeforeUpdate.value = contentBox.value.offsetHeight console.log(`beforeUpdate – old height: ${heightBeforeUpdate.value}px`) } }) // Read DOM height **after** patch onUpdated(() => { if (contentBox.value) { currentHeight.value = contentBox.value.offsetHeight console.log(`onUpdated – new height: {currentHeight.value}px`) } }) </script> <style scoped> .demo-box { max-width: 700px; margin: 3rem auto; padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); } .content-box { margin: 1.5rem 0; padding: 1rem; background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 8px; min-height: 100px; overflow: hidden; } </style> |
What you see in console & UI when you click +1 multiple times:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
beforeUpdate – old height: 120px onUpdated – new height: 145px beforeUpdate – old height: 145px onUpdated – new height: 170px ... |
→ onBeforeUpdate always sees the old height (before patch) → onUpdated sees the new height (after patch)
This is the only place you can reliably read “before” DOM state.
4. Classic & Modern Use-Cases for beforeUpdate / onBeforeUpdate
In 2026, people still use onBeforeUpdate for these rare but real cases:
- Measure DOM before patch → compare old vs new dimensions (e.g. auto-resize container)
- Save scroll position before content changes (e.g. chat message added → keep scroll at bottom)
- Debugging — log what changed before re-render
- Third-party lib sync — tell a library “data is about to change” before DOM patch
- Performance optimization — skip expensive work if you can detect no visible change
Modern real-world example (2026 style – scroll position preservation)
|
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<script setup lang="ts"> import { ref, onBeforeUpdate, onUpdated, nextTick } from 'vue' const messages = ref([ 'Hello', 'How are you?', // ... ]) const chatContainer = ref<HTMLElement | null>(null) let savedScrollHeight = 0 let wasAtBottom = true onBeforeUpdate(() => { if (chatContainer.value) { const el = chatContainer.value // Was user already at bottom? wasAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 10 savedScrollHeight = el.scrollHeight } }) onUpdated(async () => { if (chatContainer.value) { const el = chatContainer.value await nextTick() // wait for DOM to settle if (wasAtBottom) { // Keep scrolled to bottom after new messages el.scrollTop = el.scrollHeight } else { // Restore relative position if user was reading middle const newScrollHeight = el.scrollHeight el.scrollTop += newScrollHeight - savedScrollHeight } } }) </script> <template> <div ref="chatContainer" class="chat-window"> <div v-for="msg in messages" :key="msg" class="message"> {{ msg }} </div> </div> </template> |
→ onBeforeUpdate saves old scroll state → onUpdated restores position intelligently
5. Quick Summary Table – beforeUpdate in 2026
| Question | Answer / Reality in 2026 |
|---|---|
| When does it run? | Every time before re-render / DOM patch |
| Is data/props/computed updated? | Yes — new values are already set |
| Is DOM updated yet? | No — still shows old state |
| Is $el / refs available? | Yes — you can read old DOM |
| Do modern developers use it? | Rarely — only for special DOM-before-patch cases |
| Modern replacement | onBeforeUpdate(() => { … }) |
| Still asked in interviews? | Yes — “What is the difference between beforeUpdate & updated?” |
Pro Tips from Real Projects (Hyderabad 2026)
- Most used hooks → onMounted (init), onBeforeUnmount (cleanup), onUpdated (post-DOM measurement)
- Use onBeforeUpdate only when you must read old DOM state before patch
- Never mutate state in onBeforeUpdate or onUpdated — causes infinite loop
- Combine onBeforeUpdate + onUpdated + nextTick() for precise scroll / size restoration
- In SSR / Nuxt → onBeforeUpdate & onUpdated run only client-side
- Avoid overusing — 95% of components never need beforeUpdate or updated
Your mini homework:
- Create the scroll-preservation example above
- Add many messages rapidly → see scroll behavior with & without onBeforeUpdate
- Add console.log in onBeforeUpdate & onUpdated → compare heights
- Try mutating messages in onUpdated → see infinite loop warning
Any part confusing? Want full examples for:
- onBeforeUpdate + onUpdated for auto-resize textarea?
- Scroll restoration in chat / infinite list?
- beforeUpdate vs updated performance measurement?
- Lifecycle in <KeepAlive> / <Transition> / <Suspense>?
Just tell me — we’ll trace the lifecycle and build real examples together step by step 🚀
