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,onBeforeUnmountonUpdated,onBeforeUpdateonActivated,onDeactivatedonErrorCaptured,onRenderTracked,onRenderTriggeredwatch,watchEffect,watchPostEffect,watchSyncEffectprovide,injectgetCurrentInstance
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
awaitstatements - 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
onMountedor after hook registration