Vue lifecycle hooks let you run code at specific moments in a component's life, such as after it mounts to the DOM, after state triggers a DOM update, or right before it unmounts.
In <script setup>, you import lifecycle functions like onMounted and onUnmounted from 'vue'. Notice there are no onCreated or onBeforeCreate hooks because top-level code inside <script setup> already runs during component creation.
flowchart LR
Setup["script setup runs"] --> Mount["onMounted (DOM Ready)"]
Mount --> Update["onUpdated (After DOM Patch)"]
Mount --> Unmount["onUnmounted (Cleanup)"]
| Options API Hook | Composition API Hook | When It Runs |
|---|---|---|
beforeCreate / created |
Top-level <script setup> |
During component setup |
beforeMount |
onBeforeMount() |
Right before first DOM render |
mounted |
onMounted() |
After DOM nodes are mounted |
beforeUpdate |
onBeforeUpdate() |
Before DOM patches on state change |
updated |
onUpdated() |
After reactive DOM patch finishes |
unmounted |
onUnmounted() |
After component leaves the DOM |
Pairing setup logic with cleanup inside onUnmounted keeps long-lived timers and event listeners under control. This timer component starts an interval inside onMounted and clears that interval inside onUnmounted so background timers never leak memory.
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue';
const timerSeconds = ref(0);
let intervalId = null;
onMounted(() => {
intervalId = setInterval(() => {
timerSeconds.value++;
}, 1000);
});
onUpdated(() => {
console.log('DOM updated at:', timerSeconds.value);
});
onUnmounted(() => {
if (intervalId) {
clearInterval(intervalId);
}
});
</script>
<template>
<div class="timer-box">
<h3>Active Session Timer</h3>
<p>Elapsed Time: <strong>{{ timerSeconds }}s</strong></p>
</div>
</template>
onUnmounted: Leaving setInterval, window.addEventListener, or WebSocket connections open after a component unmounts causes memory leaks and duplicate callbacks.onUpdated: Changing reactive state inside onUpdated() triggers another render cycle, which can trap your component in an infinite update loop. Use watch() instead.onMounted() after an await inside <script setup> fails because Vue loses track of the current component instance. Register all hooks synchronously at the top level.<script setup>?Call your fetch function inside onMounted() for client-side data loading, or invoke it at the top level if using <Suspense> with async setup.
onMounted() multiple times in one component?Yes. Composables and <script setup> can each register their own onMounted() and onUnmounted() callbacks, and Vue runs every registered hook in order.
No. onMounted, onUpdated, and onUnmounted only run in the browser because there is no browser DOM on the server.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With