| name | unhead-vue-skilld |
| description | Full-stack head manager built for Vue. ALWAYS use when writing code importing "@unhead/vue". Consult for debugging, best practices, or modifying @unhead/vue, unhead/vue, unhead vue, unhead. |
| metadata | {"version":"3.1.0","generated_at":"2026-04-27T00:00:00.000Z","references_synced_at":"2026-04-27T00:00:00.000Z"} |
unjs/unhead @unhead/vue@3.1.0
Tags: next: 3.0.0-beta.9, beta: 3.0.0-beta.12, rc: 3.0.0-rc.4
References: Docs
API Changes
This section documents version-specific API changes — prioritize recent major/minor releases.
-
BREAKING: createHead() and createServerHead() removed from @unhead/vue root in v2 — use subpath imports: createHead() from @unhead/vue/client (SPA) or @unhead/vue/server (SSR); createServerHead() no longer exists source
-
BREAKING: Implicit context removed in v2 — setHeadInjectionHandler() deleted; useHead() called after an await in lifecycle hooks (e.g. onMounted) throws because Vue context is lost; wrap async data fetching before calling useHead() source
-
BREAKING: vmid and hid tag properties removed in v2 — use key for deduplication: script: [{ key: 'my-key' }] source
-
BREAKING: children tag property removed in v2 — use innerHTML instead source
-
BREAKING: body: true tag property removed in v2 — use tagPosition: 'bodyClose' instead source
-
BREAKING: useScript() no longer returns a Promise in v2 — .then() calls silently fail; use .onLoaded(() => ...) instead source
-
BREAKING: useScript() API no longer accessible directly on the instance in v2 — must use .proxy explicitly: script.proxy.myFn() not script.myFn(); code compiles but calls are lost at runtime source
-
BREAKING: stub() option and script:instance-fn hook removed from useScript() in v2 — replace with custom use() logic source
-
BREAKING: Promise inputs in useHead() no longer auto-resolved in v2 — await the promise before passing, or opt in to PromisePlugin from @unhead/vue/plugins source
-
BREAKING: TemplateParamsPlugin and AliasSortingPlugin no longer built-in in v2 — must opt in: createHead({ plugins: [TemplateParamsPlugin, AliasSortingPlugin] }) imported from @unhead/vue/plugins source
-
BREAKING: Capo.js tag sorting is now the default in v2 — breaks snapshot tests; opt out with createHead({ disableCapoSorting: true }) source
-
DEPRECATED: useServerHead(), useServerHeadSafe(), useServerSeoMeta() — use useHead(), useHeadSafe(), useSeoMeta() with import.meta.server conditionals or { mode: 'server' } option for tree-shaking
-
NEW: <Head>, <Title>, <Meta>, <Link>, <Script> template components — import from @unhead/vue/components source
-
NEW: DeprecationsPlugin from @unhead/vue/plugins — re-enables removed vmid, hid, children, body properties for gradual migration to v2 source
Also changed: @unhead/schema deprecated — use @unhead/vue/types instead · createHeadCore deprecated — use createUnhead · Default SSR tags auto-inserted in v2 (charset, viewport, html lang="en"); disable with createHead({ disableDefaults: true }) · CJS exports removed, ESM only · Vue 2 support removed · useHead() context lost after async in Vue lifecycle hooks — fetch data first, then call useHead()
Best Practices
-
Always use injectHead() from @unhead/vue instead of getActiveHead() from unhead in Vue components — injectHead() binds to the Vue component context (visible in onServerPrefetch), while getActiveHead() returns a shared cross-request instance that breaks in SSR. The maintainer confirmed this is the correct approach for Vue. source
-
Avoid calling useHead() inside watchers — each call creates a new entry rather than updating the existing one, leading to duplicate entries. Instead, pass reactive refs or computed getters directly to a single useHead() call at setup time so updates flow automatically. source
-
When useHead() must be called after async operations (e.g. inside onMounted), capture the head instance at setup time with injectHead() and pass it as { head } in the second argument — Vue's inject context is lost after await. For most cases, prefer the reactive state pattern: define useHead() once at setup with computed getters, and update a ref asynchronously. source
-
Use useHeadSafe() instead of useHead() whenever head input comes from user-provided or third-party sources — it enforces an attribute whitelist and strips script tags and event handlers, preventing XSS without requiring manual sanitization. source
-
Add the UnheadVite() plugin from @unhead/addons/vite to your Vite config for Vue apps — it tree-shakes server-only composables from the client build and transforms useSeoMeta() calls into raw useHead() calls, saving ~3kb. Nuxt configures this automatically; standalone Vue apps must opt in.