Fix: Function Called Without Active Instance in Vue.js

Error message:
{calledFunctionName}() called without active instance.
Lifecycle & App 2025-01-25

What Causes This Error?

This error occurs when you call Vue Composition API functions like onMounted, onUnmounted, watch, computed, or other hooks outside of the setup() function or after an await statement that breaks the synchronous setup flow.

The Problem

import { onMounted } from 'vue'

// ❌ Called outside setup
onMounted(() => {
  console.log('mounted')
}) // Error: onMounted called without active instance

export default {
  setup() {
    // Too late!
  }
}
export default {
  async setup() {
    await fetchData()

    // ❌ Called after await - instance context lost
    onMounted(() => {
      console.log('mounted')
    })
  }
}

The Fix

Call Hooks Synchronously in Setup

import { onMounted, ref } from 'vue'

export default {
  setup() {
    const data = ref(null)

    // ✅ Called synchronously in setup
    onMounted(() => {
      console.log('Component mounted')
    })

    return { data }
  }
}

With Script Setup

<script setup>
import { onMounted, onUnmounted, ref } from 'vue'

const data = ref(null)

// ✅ Top-level in script setup works
onMounted(() => {
  console.log('Component mounted')
})

onUnmounted(() => {
  console.log('Component unmounted')
})
</script>

Register Hooks Before Await

export default {
  async setup() {
    const data = ref(null)

    // ✅ Register hooks BEFORE any await
    onMounted(() => {
      console.log('mounted')
    })

    onUnmounted(() => {
      console.log('unmounted')
    })

    // Now you can use await
    await fetchInitialData()

    return { data }
  }
}

Common Scenarios

In Composables

// ❌ Wrong - composable called outside setup
// utils/useTimer.js
export function useTimer() {
  const count = ref(0)

  onMounted(() => { // Will fail if called outside setup!
    setInterval(() => count.value++, 1000)
  })

  return { count }
}

// Usage outside setup
const { count } = useTimer() // Error!

// ✅ Correct - call composable inside setup
export default {
  setup() {
    const { count } = useTimer() // ✅ Works!
    return { count }
  }
}

In Async Callbacks

// ❌ Wrong
export default {
  setup() {
    setTimeout(() => {
      onMounted(() => {}) // Error! No active instance
    }, 0)

    fetchData().then(() => {
      watch(data, () => {}) // Error! No active instance
    })
  }
}

// ✅ Correct - register synchronously
export default {
  setup() {
    const data = ref(null)

    // Register hooks synchronously
    onMounted(async () => {
      data.value = await fetchData()
    })

    watch(data, (newVal) => {
      console.log('Data changed:', newVal)
    })

    return { data }
  }
}

In Event Handlers

// ❌ Wrong
function handleClick() {
  onMounted(() => {}) // No instance here!
}

// ✅ There's no need for onMounted in an event handler
// The component is already mounted when events fire
function handleClick() {
  // Just do your logic directly
  doSomething()
}

Store or Service Files

// ❌ Wrong - stores are typically outside component context
// stores/user.js
import { onMounted } from 'vue'

export const useUserStore = defineStore('user', () => {
  onMounted(() => {}) // Error!
})

// ✅ Correct - use store actions instead
export const useUserStore = defineStore('user', () => {
  const user = ref(null)

  async function initialize() {
    user.value = await fetchUser()
  }

  return { user, initialize }
})

// Call initialize from component setup
export default {
  setup() {
    const store = useUserStore()

    onMounted(() => {
      store.initialize()
    })
  }
}

Functions Requiring Active Instance

These functions must be called in setup:

  • onMounted, onUnmounted, onBeforeMount, onBeforeUnmount
  • onUpdated, onBeforeUpdate
  • onActivated, onDeactivated
  • onErrorCaptured, onRenderTracked, onRenderTriggered
  • watch, watchEffect, watchPostEffect, watchSyncEffect
  • provide, inject
  • getCurrentInstance

Using getCurrentInstance (Advanced)

import { getCurrentInstance } from 'vue'

export default {
  setup() {
    const instance = getCurrentInstance()

    // Can use instance later, but be careful
    setTimeout(() => {
      if (instance) {
        // Access instance properties
        console.log(instance.proxy)
      }
    }, 1000)
  }
}

Quick Checklist

  • Call lifecycle hooks synchronously in setup()
  • Register all hooks BEFORE any await statements
  • Use <script setup> for simpler code
  • Composables must be called inside setup()
  • Don’t call hooks in callbacks, event handlers, or timeouts
  • Move async logic inside onMounted or after hook registration