What Causes This Error?
This error occurs when you try to use head management functions (useHead, useSeoMeta, etc.) before Unhead has been initialized, or outside of a valid Nuxt context.
Common Causes
- Calling head composables outside of setup
- Using head functions in non-Nuxt contexts
- Plugin initialization order issues
- SSR context problems
The Fix
Ensure Head Composables Are in Setup
<script setup>
// ✅ Correct - called in script setup
useHead({
title: 'My Page'
})
useSeoMeta({
description: 'Page description'
})
</script>
Wrong: Outside Setup Context
// ❌ Wrong - outside of component
function updateTitle(title) {
useHead({ title }) // Error!
}
// ✅ Correct - pass the composable result
function usePageMeta() {
const head = useHead({
title: 'Default'
})
function updateTitle(title) {
head.patch({ title })
}
return { updateTitle }
}
Plugin Context
If using in a plugin, ensure proper context:
// ❌ Wrong - may run before Unhead is ready
export default defineNuxtPlugin(() => {
useHead({ title: 'App' }) // May fail
})
// ✅ Correct - use nuxtApp.runWithContext
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:created', () => {
nuxtApp.runWithContext(() => {
useHead({ title: 'App' })
})
})
})
Dynamic Head Updates
For dynamic updates after setup:
<script setup>
// Create the head reference in setup
const head = useHead({
title: 'Initial Title'
})
// Update later via patch
function updateTitle(newTitle) {
head.patch({
title: newTitle
})
}
// Or use computed for reactive updates
const pageTitle = ref('My Page')
useHead({
title: computed(() => pageTitle.value)
})
</script>
In Composables
// composables/usePageSeo.ts
export function usePageSeo(options: { title: string; description: string }) {
// ✅ Called within composable that's used in setup
useHead({
title: options.title
})
useSeoMeta({
description: options.description,
ogTitle: options.title,
ogDescription: options.description
})
}
// Usage in component
<script setup>
usePageSeo({
title: 'Products',
description: 'Browse our products'
})
</script>
Middleware Context
Head composables work in middleware:
// middleware/set-title.ts
export default defineNuxtRouteMiddleware((to) => {
// ✅ Works in middleware
useHead({
title: to.meta.title as string || 'Default'
})
})
Server Routes (API)
For API routes, use the event-based API:
// server/api/og-image.ts
export default defineEventHandler((event) => {
// ❌ Wrong - useHead doesn't work in server routes
useHead({ title: 'API' })
// Server routes don't render HTML,
// so head management doesn't apply
})
Check Nuxt Context
Verify you’re in a valid context:
// Verify Nuxt is available
const nuxtApp = useNuxtApp()
if (nuxtApp) {
useHead({ title: 'Safe' })
}
Multiple Unhead Instances
If you have conflicting Unhead setups:
// nuxt.config.ts
export default defineNuxtConfig({
// Ensure only one Unhead instance
experimental: {
headNext: true // Use latest head handling
}
})
Debugging
Check if Unhead is initialized:
// In a component
const nuxtApp = useNuxtApp()
console.log('Unhead available:', !!nuxtApp.vueApp._context.provides.usehead)
Alternative: definePageMeta
For static head data, use definePageMeta:
<script setup>
// ✅ Alternative for static meta
definePageMeta({
title: 'My Page',
description: 'Page description'
})
</script>
Then in layout/app:
<script setup>
const route = useRoute()
useHead({
title: computed(() => route.meta.title as string)
})
</script>
Quick Checklist
- Head composables are called in
<script setup>or setup function - Not called in callbacks/timers (use
head.patchinstead) - Plugin hooks properly await Unhead initialization
- Not using head composables in server API routes
- Check for multiple conflicting Unhead instances
- Use
computedfor reactive head values