Chapter 100: Vue ‘updated’ Lifecycle Hook
Updated
(or its modern Composition API name onUpdated)
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:
“I just changed some reactive data → Vue has finished patching / updating the real DOM → now I want to do something with the new DOM state (measure height, scroll to bottom, re-initialize a third-party widget, etc.).”
It is the counterpart to beforeUpdate / onBeforeUpdate:
- beforeUpdate = last chance before the DOM patch
- updated = first chance after the DOM patch
1. Exact position in the lifecycle (Vue 3 timeline)
|
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) ── data changed, about to re-render / patch DOM onUpdated (updated) ── ← this one – DOM has been patched & updated onBeforeUnmount (beforeUnmount) ── about to be destroyed onUnmounted (unmounted) ── component is gone |
updated / onUpdated runs every single time the component re-renders because something reactive changed — after Vue has finished diffing & patching the DOM.
2. What is available / NOT available in onUpdated?
This is the key table — memorize when you can and cannot do certain things.
| Feature / Access | Available in onUpdated()? | Reason / Explanation |
|---|---|---|
| this / component instance | Yes | Fully populated |
| data() / ref() / reactive() | Yes | Reactive state is updated (new values are set) |
| props | Yes | New prop values are received & applied |
| computed | Yes | All computed properties have been re-evaluated with new values |
| methods / functions | Yes | All functions available |
| Template compiled & rendered? | Yes | Re-render has completed |
| Real DOM / $el | Yes | DOM reflects new state after patch |
| Template refs (ref=”…”) | Yes | Refs are updated to new DOM nodes (if they changed) |
| DOM APIs (focus, scroll, measure) | Yes | Safe to call .focus(), .getBoundingClientRect(), scrollTo(), etc. — DOM is fresh |
| The DOM has been updated? | Yes | Patch / re-render is finished — DOM shows new state |
Summary sentence to remember forever:
In updated / onUpdated, the new reactive values are already set, computed properties have been re-calculated, and the DOM has already been patched to reflect the new state — this is the first safe moment to read/measure/manipulate the updated DOM.
3. Real, Practical Examples – When & How You Use onUpdated Every Week
Example 1 – Auto-scroll chat to bottom after new message (most common real use-case)
|
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 62 63 64 65 66 67 68 69 70 71 72 |
<template> <div class="chat-window" ref="chatContainer"> <div v-for="msg in messages" :key="msg.id" class="message" > {{ msg.text }} </div> <input v-model="newMessage" @keyup.enter="sendMessage" placeholder="Type a message..." /> </div> </template> <script setup lang="ts"> import { ref, watch, onUpdated, nextTick } from 'vue' const messages = ref([ { id: 1, text: 'Hello!' }, { id: 2, text: 'How are you?' } ]) const newMessage = ref('') const chatContainer = ref<HTMLElement | null>(null) function sendMessage() { if (!newMessage.value.trim()) return messages.value.push({ id: Date.now(), text: newMessage.value.trim() }) newMessage.value = '' } // Auto-scroll after every update (new message added) onUpdated(async () => { // Wait one extra tick in case child components need to finish rendering await nextTick() if (chatContainer.value) { chatContainer.value.scrollTo({ top: chatContainer.value.scrollHeight, behavior: 'smooth' }) } }) </script> <style scoped> .chat-window { height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 1rem; border-radius: 8px; margin-bottom: 1rem; } .message { margin: 0.5rem 0; padding: 0.8rem; background: #f0f0f0; border-radius: 8px; max-width: 80%; } </style> |
Why onUpdated here?
- onMounted only runs once — won’t catch new messages later
- watch(messages, …) runs before DOM patch → scrollHeight is still old
- onUpdated + nextTick runs after DOM patch → scrollHeight is correct → perfect bottom scroll
Example 2 – Measure height after content change (auto-resize container)
|
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 |
<template> <div class="content-box" ref="contentBox"> <p v-for="n in lineCount" :key="n"> Line {{ n }} — this box grows when you add lines </p> </div> <button @click="lineCount++">Add line</button> <p>Current height: {{ boxHeight }} px</p> </template> <script setup lang="ts"> import { ref, onUpdated, nextTick } from 'vue' const lineCount = ref(5) const contentBox = ref<HTMLElement | null>(null) const boxHeight = ref(0) onUpdated(async () => { await nextTick() // wait for DOM to settle after patch if (contentBox.value) { boxHeight.value = contentBox.value.offsetHeight } }) </script> <style scoped> .content-box { margin: 1.5rem 0; padding: 1rem; background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; transition: height 0.3s ease; } </style> |
→ Every time lineCount increases → onUpdated measures the new height after patch
3. Why updated / onUpdated is not used very often
Most developers avoid onUpdated because:
- It runs after every single update — can hurt performance if you do heavy work
- It’s easy to create infinite loops (mutate state inside onUpdated → triggers another update → loop)
- In most cases, onMounted + watch + nextTick covers 95% of needs
When to use onUpdated (2026 rule of thumb)
Use it only when you need to:
- Read/measure new DOM state after a patch (height, scroll position, focus state…)
- Re-initialize a third-party widget that depends on updated DOM
- Save state that depends on the post-render layout
Never use it for:
- Fetching data (use watch or onMounted)
- Setting state (causes loop)
- One-time initialization (use onMounted)
4. Quick Summary Table – updated / onUpdated in 2026
| Question | Answer / Reality in 2026 |
|---|---|
| When does it run? | After every re-render / DOM patch |
| Is data/props/computed updated? | Yes — new values are set |
| Is DOM updated? | Yes — reflects new state |
| Is $el / refs available? | Yes — updated to new DOM |
| Do modern developers use it? | Rarely — only for post-patch DOM reading/measuring |
| Modern replacement | onUpdated(() => { … }) |
| 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)
- Never mutate state in onUpdated — causes infinite loop (Vue warns in dev mode)
- Always use nextTick() inside onUpdated if you need to wait for child components to finish rendering
- In SSR / Nuxt → onUpdated runs only client-side
- Avoid overusing — 95% of components never need beforeUpdate or updated
- Combine onBeforeUpdate + onUpdated for “before & after” DOM measurement (scroll, height, etc.)
Your mini homework:
- Create the auto-scroll chat example above
- Add messages rapidly → see onUpdated + nextTick keep scroll at bottom
- Add console.log in onBeforeUpdate & onUpdated → compare scrollHeight before/after
- 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 🚀
