Chapter 97: Vue ‘beforeMount’ Lifecycle Hook
BeforeMount
This hook sits in a very sweet spot in the lifecycle — it is the last moment you can run code before Vue actually renders the component into the real DOM and before any DOM-related side-effects can happen.
In modern Vue 3 + <script setup> code (2026 standard) this hook is still very frequently used, although many simple cases can now be handled directly at the top level of <script setup>.
1. Exact position in the lifecycle (Vue 3 timeline)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
beforeCreate() ── almost nothing ready created() ── data, props, computed, methods ready – but still no DOM beforeMount() ── ← this one – template compiled, render function ready, about to insert into DOM mounted() ── DOM is now live – first time you can safely touch $el / refs beforeUpdate() ── data changed, about to re-render updated() ── DOM patched beforeUnmount() ── about to be destroyed unmounted() ── gone |
beforeMount is the last safe moment before the component becomes visible in the browser.
2. What is available / NOT available in beforeMount?
This is the key table — memorize when you can and cannot do certain things.
| Feature / Access | Available in beforeMount()? | Reason / Explanation |
|---|---|---|
| this / component instance | Yes | Fully populated |
| data() / ref() / reactive() | Yes | Reactive state is ready |
| props | Yes | Props resolved & injected |
| computed | Yes | All computed properties created & cached |
| methods | Yes | All methods attached |
| Template compiled? | Yes | render function is ready |
| Real DOM / $el | No | Mounting has not happened yet — $el is still undefined |
| Template refs (ref=”…”) | No | Refs are not populated yet |
| DOM APIs (getElementById, etc.) | No | No elements in document yet |
| this.$emit | Yes | You can emit (but parent may not see it yet) |
| this.$parent / $root | Yes | Relationships established |
| Injected values (provide/inject) | Yes | Injection already happened |
Summary sentence to remember forever:
In beforeMount, everything JavaScript-related is ready, the template is compiled, but the component is not yet in the DOM — so no $el, no refs, no DOM measurement, no DOM manipulation is possible yet.
3. Real Example – Seeing What Is / Isn’t Available
|
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 |
<template> <div ref="rootEl" class="lifecycle-demo"> <h2>beforeMount Hook Demo</h2> <p>Check console logs to see what is ready at each stage</p> <p>Count: {{ count }}</p> <button @click="count++">+1</button> </div> </template> <script setup> import { ref, onBeforeMount, onMounted } from 'vue' const count = ref(0) const rootEl = ref(null) console.log('Top level <script setup> – runs BEFORE beforeMount') onBeforeMount(() => { console.group('onBeforeMount') console.log('count.value:', count.value) // 0 – reactive state ready console.log('rootEl.value:', rootEl.value) // null – no DOM yet! console.log('Can I access DOM?', document.getElementById('app')) // null or wrong element try { console.log('rootEl height:', rootEl.value?.offsetHeight) // TypeError – null } catch (err) { console.log('Error:', err.message) } console.groupEnd() }) onMounted(() => { console.group('onMounted') console.log('rootEl.value:', rootEl.value) // <div> element – now exists! console.log('rootEl height:', rootEl.value.offsetHeight) // real number console.groupEnd() }) </script> <style scoped> .lifecycle-demo { padding: 2rem; max-width: 600px; margin: 2rem auto; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); } </style> |
Console output when component mounts:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Top level <script setup> – runs BEFORE beforeMount onBeforeMount count.value: 0 rootEl.value: null Can I access DOM? null or wrong element Error: Cannot read properties of null (reading 'offsetHeight') onMounted rootEl.value: <div class="lifecycle-demo">… rootEl height: 150 (or whatever real height) |
→ You see clearly: beforeMount has reactive state, but no DOM access
4. Classic Use-Cases for beforeMount / onBeforeMount (still valid in 2026)
In modern code people still use onBeforeMount for:
- Last-minute state preparation that depends on props/data but before DOM is touched
- Setting up non-DOM related side-effects (e.g. start a timer, register a global shortcut listener)
- Normalizing/transforming props into local reactive state (rare now)
- Old code patterns that want to run after props/data but before render
Modern real-world example (2026 style)
|
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> import { ref, onBeforeMount } from 'vue' const user = ref(null) const isLoading = ref(true) onBeforeMount(async () => { // Last moment before DOM render – good place for initial fetch if you don't want suspense try { const res = await fetch('/api/user') user.value = await res.json() } catch (err) { console.error(err) } finally { isLoading.value = false } }) </script> <template> <div v-if="isLoading">Loading user...</div> <div v-else> Welcome, {{ user?.name }} </div> </template> |
→ onBeforeMount here is used instead of top-level await (which would block rendering)
5. Why created was more popular in Vue 2, but beforeMount is preferred now
In Vue 2 / early Vue 3:
- People used created for almost everything (fetch, normalize props…)
- But created runs before template compilation → no access to template refs or render context
In Vue 3 + <script setup>:
- Top-level code runs very early (even before beforeCreate)
- onBeforeMount gives you one last chance to run code before DOM insertion
- Most people now do simple init at top level, heavy init in onMounted
Realistic 2026 interview answer:
“beforeMount / onBeforeMount runs after setup, data, props, computed are ready and the template is compiled, but before the component is inserted into the DOM. It’s the last moment you can modify state before first render. In modern code we rarely need it — top-level <script setup> or onMounted usually cover all cases. It’s most useful for last-minute setup that must happen before DOM paint.”
Quick Summary Table – beforeMount in 2026
| Question | Answer / Reality in 2026 |
|---|---|
| When does it run? | After setup/data/props/computed, before first DOM insert |
| Is data/props/computed available? | Yes |
| Is DOM / $el / refs available? | No |
| Is this available? | Yes (in Options API) |
| Do modern developers use it? | Rarely — onBeforeMount is still used sometimes |
| Modern replacement | Top-level <script setup> code or onMounted |
| Still asked in interviews? | Yes — to check if you understand full lifecycle |
Final 2026 Advice
- In new code → prefer top-level <script setup> code for simple init, onMounted for DOM-related init
- Use onBeforeMount only when you need to modify state one last time before first render
- When you see beforeMount or created → it means Options API (legacy style)
- Learn to read it — many jobs, old projects, tutorials still use it
- Never teach beginners beforeCreate / created / beforeMount as primary — start with onMounted & top-level code
Your mini homework:
- Create a component with beforeCreate, created, beforeMount, mounted logging
- Try to access ref value in each → see when it becomes available
- Add onBeforeMount to set some state → see it works before first render
Any part confusing? Want to see:
- Full lifecycle log comparison (Options vs Composition)?
- Real legacy code using created / beforeMount → how to refactor?
- beforeMount vs top-level <script setup> code?
- When beforeMount is actually useful (rare modern cases)?
Just tell me — we’ll trace the lifecycle and refactor together step by step 🚀
