Fix: Nuxt Instance Unavailable - Composable Called Outside Setup

Error message:
A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.
composables 2025-01-25

What Causes This Error?

This is one of the most common Nuxt errors. It occurs when you call a Nuxt composable (like useFetch, useRoute, useState, etc.) in a place where the Nuxt context is not available.

The Rule

Nuxt composables must be called:

  • Inside <script setup>
  • Inside Vue’s setup() function
  • Inside Nuxt plugins
  • Inside Nuxt middleware
  • Inside Nuxt hooks

Common Mistakes and Fixes

Mistake 1: Calling Composables in Callbacks

// ❌ Wrong - composable called inside setTimeout
<script setup>
setTimeout(() => {
  const route = useRoute() // ERROR!
}, 1000)
</script>

// ✅ Correct - call composable at top level
<script setup>
const route = useRoute()

setTimeout(() => {
  console.log(route.path) // Use the already-created ref
}, 1000)
</script>

Mistake 2: Calling Composables in Event Handlers

// ❌ Wrong
<script setup>
const handleClick = () => {
  const { data } = useFetch('/api/data') // ERROR!
}
</script>

// ✅ Correct - use $fetch for on-demand requests
<script setup>
const handleClick = async () => {
  const data = await $fetch('/api/data')
}
</script>

Mistake 3: Calling Composables in Regular Functions

// ❌ Wrong - in a utility file
// utils/helpers.js
export function getData() {
  const { data } = useFetch('/api/data') // ERROR!
  return data
}

// ✅ Correct - pass the data or use $fetch
// utils/helpers.js
export async function getData() {
  return await $fetch('/api/data')
}

Mistake 4: Async/Await Breaking Context

// ❌ Wrong - context lost after await
<script setup>
await someAsyncOperation()
const route = useRoute() // May fail!
</script>

// ✅ Correct - call composables before await
<script setup>
const route = useRoute()
await someAsyncOperation()
// Now use route safely
</script>

Mistake 5: Using Composables in External Libraries

// ❌ Wrong - in a Pinia store action
actions: {
  async fetchData() {
    const { data } = useFetch('/api/data') // ERROR!
  }
}

// ✅ Correct - use $fetch or pass nuxt app
actions: {
  async fetchData() {
    const data = await $fetch('/api/data')
    this.data = data
  }
}

Production vs Development

In development, you get the detailed error message. In production, you’ll see:

[nuxt] instance unavailable

This is the same error, just minified for production.

Working with Callbacks

If you need Nuxt context in a callback, capture it first:

<script setup>
const nuxtApp = useNuxtApp()
const route = useRoute()

// Now you can use these in callbacks
onMounted(() => {
  // Safe to use route here
  console.log(route.path)
})

watch(someRef, () => {
  // Safe to use nuxtApp here
  nuxtApp.$toast.show('Changed!')
})
</script>

In Pinia Stores

Use the $fetch utility or accept context as a parameter:

// stores/user.js
export const useUserStore = defineStore('user', {
  actions: {
    // ✅ Use $fetch directly
    async fetchUser() {
      this.user = await $fetch('/api/user')
    },

    // ✅ Or accept nuxtApp as parameter
    async fetchWithContext(nuxtApp) {
      // Now you have access to nuxtApp
    }
  }
})

Quick Reference

LocationComposables Allowed?
<script setup>✅ Yes
setup() function✅ Yes
Nuxt plugins✅ Yes
Nuxt middleware✅ Yes
onMounted callback⚠️ Only if declared at top level first
setTimeout callback❌ No
Event handlers❌ No (use $fetch instead)
External utility files❌ No
Pinia actions❌ No (use $fetch instead)