Vue.js Component Lifecycle

Every Vue component goes through a series of stages from birth to removal. These stages are called the component lifecycle. Vue gives you special functions called lifecycle hooks that run automatically at each stage, letting you run code at exactly the right moment.

The Lifecycle — A Diagram Overview

Diagram: Full Component Lifecycle

Component created
       │
       ▼
  beforeCreate()     ← Runs before data and events are set up
       │
       ▼
   created()         ← Data is ready. DOM not yet rendered.
       │
       ▼
  beforeMount()      ← About to insert into the real DOM
       │
       ▼
   mounted()         ← Component is visible on screen ← Most used hook
       │
       │ (data changes trigger re-renders)
       ▼
  beforeUpdate()     ← Data changed, DOM not updated yet
       │
       ▼
   updated()         ← DOM updated to match new data
       │
       │ (component is being removed)
       ▼
  beforeUnmount()    ← Component about to be removed
       │
       ▼
  unmounted()        ← Component fully removed from DOM

beforeCreate

This hook runs before the component initializes its data or events. The data() properties and methods do not exist yet. This hook is rarely used directly in most applications.

Vue.createApp({
  beforeCreate() {
    console.log("beforeCreate: data is not available yet.");
    // this.message → undefined (data not set up)
  },
  data() {
    return { message: "Hello" };
  }
}).mount("#app");

created

The created hook runs after Vue sets up data, computed properties, methods, and watchers. The component is not yet visible on the screen, but you can access all reactive data. This is the right place to fetch data from an API when the page loads.

Vue.createApp({
  data() {
    return { users: [] };
  },
  created() {
    // Data is ready. DOM is not. Good place for API calls.
    console.log("created: loading users...");
    // fetch("/api/users").then(...)
  }
}).mount("#app");

Diagram: created vs mounted for API Calls

created():
  ✓ data properties ready
  ✗ DOM not rendered
  Best for: starting API calls early

mounted():
  ✓ data properties ready
  ✓ DOM is rendered
  Best for: accessing DOM elements, third-party libraries

For most API loading, either works. created() starts the fetch slightly earlier.

beforeMount

This hook runs right before Vue renders the component and inserts it into the DOM. The template is compiled but not yet added to the page. This hook has limited practical use for most developers.

mounted

mounted is the most commonly used lifecycle hook. It runs after Vue finishes rendering the component and inserting it into the real DOM. The component is fully visible to the user.

Use mounted() for:

  • Accessing or manipulating DOM elements directly
  • Initializing third-party libraries (charts, maps, sliders)
  • Setting up timers or intervals
  • Subscribing to external data streams
<div id="app">
  <canvas id="myChart"></canvas>
  <p>Loaded at: {{ loadTime }}</p>
</div>

<script>
  Vue.createApp({
    data() {
      return { loadTime: "" };
    },
    mounted() {
      // DOM is ready — we can access elements
      this.loadTime = new Date().toLocaleTimeString();
      
      // Example: initialize a chart library
      // const ctx = document.getElementById("myChart");
      // new Chart(ctx, { ... });
    }
  }).mount("#app");
</script>

beforeUpdate and updated

These hooks run whenever the component's reactive data changes and Vue re-renders the DOM.

  • beforeUpdate() — Data has changed, but the DOM still shows the old values. Useful for capturing the DOM state before a re-render.
  • updated() — DOM is now synchronized with the new data. Avoid modifying data here — it can trigger an infinite update loop.
Vue.createApp({
  data() {
    return { count: 0 };
  },
  beforeUpdate() {
    console.log("About to update. DOM still shows old count.");
  },
  updated() {
    console.log("DOM updated. New count is now visible.");
  },
  template: `
    <div>
      <p>Count: {{ count }}</p>
      <button @click="count++">Increment</button>
    </div>
  `
}).mount("#app");

Diagram: Update Cycle

User clicks "Increment"
        │
        ▼
count changes (count = 1)
        │
        ▼
beforeUpdate() fires   ← DOM still shows "Count: 0"
        │
        ▼
Vue re-renders DOM     ← DOM now shows "Count: 1"
        │
        ▼
updated() fires        ← DOM shows "Count: 1"

beforeUnmount and unmounted

These hooks run when a component is being removed from the page — for example, when a v-if condition becomes false.

  • beforeUnmount() — Component is about to be removed. The DOM still exists. Clean up here: clear timers, remove event listeners, cancel pending API requests.
  • unmounted() — Component has been fully removed from the DOM.
Vue.createApp({
  data() {
    return { timerID: null, seconds: 0 };
  },
  mounted() {
    // Start a timer when the component appears
    this.timerID = setInterval(() => {
      this.seconds++;
    }, 1000);
  },
  beforeUnmount() {
    // Stop the timer before the component disappears
    clearInterval(this.timerID);
    console.log("Timer cleared. Component leaving.");
  },
  template: `<p>Running for {{ seconds }} seconds.</p>`
}).mount("#app");

Diagram: Why Cleanup Matters

Component mounts → setInterval starts → runs every 1 second

Without cleanup:
  Component removed from DOM
  setInterval still running in memory
  → Memory leak
  → Error when interval tries to update destroyed component

With beforeUnmount() cleanup:
  Component about to be removed
  clearInterval(this.timerID)
  → Interval stopped
  → No memory leak

Lifecycle Hooks in Practice

Practical Use Case: Live Clock

app.component("live-clock", {
  data() {
    return {
      currentTime: "",
      intervalID: null
    };
  },
  mounted() {
    // Update the time every second
    this.updateTime();
    this.intervalID = setInterval(this.updateTime, 1000);
  },
  beforeUnmount() {
    // Stop the interval when the clock is removed
    clearInterval(this.intervalID);
  },
  methods: {
    updateTime() {
      this.currentTime = new Date().toLocaleTimeString();
    }
  },
  template: `<p>Current Time: {{ currentTime }}</p>`
});

Diagram: Clock Lifecycle

Component mounts:
  updateTime() → currentTime = "10:30:00"
  setInterval starts → calls updateTime every 1s

Every second:
  updateTime() runs
  currentTime = "10:30:01", "10:30:02", etc.
  Vue re-renders the <p> tag

Component removed (e.g., user navigates away):
  beforeUnmount() → clearInterval(this.intervalID)
  Clock stops. No further DOM updates.

Lifecycle Hook Summary Table

┌──────────────────┬────────────────────────────────────────────┐
│ Hook             │ When It Runs / Best Use                    │
├──────────────────┼────────────────────────────────────────────┤
│ beforeCreate     │ Before data setup. Rarely used.            │
│ created          │ Data ready, no DOM. Good for early API     │
│                  │ calls.                                     │
│ beforeMount      │ Just before DOM insert. Rarely used.       │
│ mounted          │ DOM ready. Use for DOM access, libraries,  │
│                  │ timers, subscriptions.                     │
│ beforeUpdate     │ Data changed, old DOM visible. Capture     │
│                  │ pre-update state.                          │
│ updated          │ DOM updated. Avoid data changes here.      │
│ beforeUnmount    │ Before removal. CLEAN UP: timers,          │
│                  │ listeners, connections.                    │
│ unmounted        │ Component fully removed.                   │
└──────────────────┴────────────────────────────────────────────┘

Summary

Vue component lifecycle hooks let you run code at specific stages of a component's life. Use created() for early data fetching and mounted() when you need the DOM to exist. The update hooks (beforeUpdate and updated) react to data changes. Always clean up timers, intervals, and event listeners in beforeUnmount() to prevent memory leaks. Understanding the lifecycle helps you write code that runs at exactly the right time and avoids hard-to-find bugs.

Leave a Comment

Your email address will not be published. Required fields are marked *