Component Lifecycle Hooks

+15 Mana ✨

Every Vue component goes through a series of initialization steps—setting up data, compiling templates, mounting to the DOM, and updating when data changes. Along the way, Vue runs lifecycle hooks that let you execute code at specific stages.

The Component Lifecycle

┌─────────────────────────────────────────┐
│              Component Created           │
│         (reactive data is set up)        │
└─────────────────────────────────────────┘
                    │
                    ▼
         ┌─────────────────────┐
         │    onBeforeMount    │
         │ (before DOM insert) │
         └─────────────────────┘
                    │
                    ▼
         ┌─────────────────────┐
         │      onMounted      │
         │   (DOM is ready)    │  ← Access DOM here
         └─────────────────────┘
                    │
           ┌───────┴───────┐
           │   Updates?    │
           └───────┬───────┘
                   │ yes
                   ▼
         ┌─────────────────────┐
         │   onBeforeUpdate    │
         │  (before DOM patch) │
         └─────────────────────┘
                    │
                    ▼
         ┌─────────────────────┐
         │      onUpdated      │
         │ (DOM is re-rendered)│
         └─────────────────────┘
                    │
           ┌───────┴───────┐
           │   Unmount?    │
           └───────┬───────┘
                   │ yes
                   ▼
         ┌─────────────────────┐
         │   onBeforeUnmount   │
         │  (still functional) │
         └─────────────────────┘
                    │
                    ▼
         ┌─────────────────────┐
         │     onUnmounted     │
         │   (cleanup time)    │  ← Cleanup here
         └─────────────────────┘

onMounted - Most Common Hook

onMounted() runs after the component is added to the DOM. Use it for:

  • Fetching initial data
  • Setting up event listeners
  • Integrating third-party libraries
  • Accessing DOM elements
vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'

const users = ref<User[]>([])
const isLoading = ref(true)
const error = ref<string | null>(null)

onMounted(async () => {
  try {
    const response = await fetch('/api/users')
    users.value = await response.json()
  } catch (e) {
    error.value = 'Failed to load users'
  } finally {
    isLoading.value = false
  }
})
</script>

<template>
  <div v-if="isLoading">Loading...</div>
  <div v-else-if="error">{{ error }}</div>
  <ul v-else>
    <li v-for="user in users" :key="user.id">{{ user.name }}</li>
  </ul>
</template>

onUnmounted - Cleanup

onUnmounted() runs when the component is removed. Use it to:

  • Remove event listeners
  • Clear intervals/timeouts
  • Disconnect from WebSockets
  • Cancel pending requests
vue
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'

const mouseX = ref(0)
const mouseY = ref(0)

function handleMouseMove(event: MouseEvent) {
  mouseX.value = event.clientX
  mouseY.value = event.clientY
}

onMounted(() => {
  // Add event listener when mounted
  window.addEventListener('mousemove', handleMouseMove)
})

onUnmounted(() => {
  // Remove event listener when unmounted
  window.removeEventListener('mousemove', handleMouseMove)
})
</script>

<template>
  <p>Mouse position: {{ mouseX }}, {{ mouseY }}</p>
</template>

onBeforeMount & onBeforeUnmount

These run just before mounting/unmounting:

vue
<script setup lang="ts">
import { onBeforeMount, onBeforeUnmount } from 'vue'

onBeforeMount(() => {
  console.log('Component is about to be mounted')
  // DOM is not yet available
})

onBeforeUnmount(() => {
  console.log('Component is about to be unmounted')
  // Component is still fully functional
})
</script>

onUpdated & onBeforeUpdate

These run when the component re-renders:

vue
<script setup lang="ts">
import { ref, onUpdated, onBeforeUpdate } from 'vue'

const count = ref(0)

onBeforeUpdate(() => {
  console.log('About to update, count is still:', count.value)
})

onUpdated(() => {
  console.log('Updated! DOM now reflects:', count.value)
})
</script>

<template>
  <button @click="count++">Count: {{ count }}</button>
</template>

Warning: Don't mutate state inside onUpdated—it can cause infinite loops!

Practical Example: Auto-Save

vue
<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted } from 'vue'

const content = ref('')
const lastSaved = ref<Date | null>(null)
const saveStatus = ref<'idle' | 'saving' | 'saved'>('idle')

let autoSaveInterval: number | null = null

async function save() {
  if (!content.value) return
  
  saveStatus.value = 'saving'
  try {
    await fetch('/api/document', {
      method: 'POST',
      body: JSON.stringify({ content: content.value })
    })
    lastSaved.value = new Date()
    saveStatus.value = 'saved'
  } catch (e) {
    console.error('Save failed:', e)
    saveStatus.value = 'idle'
  }
}

onMounted(() => {
  // Load existing content
  fetch('/api/document')
    .then(res => res.json())
    .then(data => {
      content.value = data.content || ''
    })
  
  // Set up auto-save every 30 seconds
  autoSaveInterval = window.setInterval(save, 30000)
})

onUnmounted(() => {
  // Clean up interval
  if (autoSaveInterval) {
    clearInterval(autoSaveInterval)
  }
  
  // Save on unmount
  save()
})

// Also save when content changes (debounced by watching)
let saveTimeout: number | null = null
watch(content, () => {
  saveStatus.value = 'idle'
  if (saveTimeout) clearTimeout(saveTimeout)
  saveTimeout = window.setTimeout(save, 2000)
})
</script>

<template>
  <div class="editor">
    <div class="toolbar">
      <span v-if="saveStatus === 'saving'">Saving...</span>
      <span v-else-if="saveStatus === 'saved'">
        Saved at {{ lastSaved?.toLocaleTimeString() }}
      </span>
      <button @click="save">Save Now</button>
    </div>
    <textarea v-model="content" placeholder="Start writing..."></textarea>
  </div>
</template>

All Lifecycle Hooks Summary

HookWhen it runsCommon uses
onBeforeMountBefore DOM insertionPre-mount setup
onMountedAfter DOM insertionFetch data, DOM access
onBeforeUpdateBefore re-renderRead DOM before update
onUpdatedAfter re-renderDOM-dependent operations
onBeforeUnmountBefore removalLast-minute cleanup
onUnmountedAfter removalRemove listeners, cleanup
✓ Completed