unhead-vue-skilld

SKILL.md

unjs/unhead @unhead/vue

Full-stack manager built for Vue.

Version: 2.1.12 (Mar 2026) Deps: hookable@^6.0.1, unhead@2.1.12 Tags: next: 3.0.0-beta.9 (Feb 2026), beta: 3.0.0-beta.12 (Mar 2026), latest: 2.1.12 (Mar 2026)

References: Docs — API reference, guides

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. source

  • Pass { mode: 'server' } to useHead() for static SEO metadata (Open Graph images, robots, schema.org) that doesn't need client-side reactivity — this strips the tags from the client bundle entirely. Similarly use { mode: 'client' } for analytics scripts to keep them out of SSR output. Caveat: titleTemplate must be included in both environments to avoid title flashing. source

  • Use tagPosition: 'bodyClose' for non-critical scripts (analytics, chat widgets) instead of head — this prevents render-blocking and improves page load performance. Use tagPriority: 'critical' | 'high' | 'low' aliases rather than raw numbers to preserve Capo.js-derived ordering weights that Unhead applies automatically. source

  • Use textContent instead of innerHTML for inline scripts and styles — textContent escapes HTML characters, preventing injection. Only use innerHTML when HTML entities are required, and sanitize the content yourself (e.g. with DOMPurify). For user-generated inline content, prefer useHeadSafe() which restricts scripts to type="application/json" only. source

  • Register TemplateParamsPlugin and define global templateParams (e.g. siteName, separator) once in your head instance setup rather than repeating them per page. These params work across all head tags — including og:title and meta descriptions — not just titleTemplate. Set %separator to a smart separator like · or ; it auto-removes when adjacent to empty content. source

  • Use InferSeoMetaPlugin to automatically derive og:title and og:description from existing title and description tags, eliminating manual duplication. Configure ogTitle with a transform function to strip the site name suffix from Open Graph titles (e.g. removing "| My Site" that titleTemplate appends). source

Weekly Installs
19
GitHub Stars
141
First Seen
14 days ago
Installed on
claude-code17
cursor17
gemini-cli16
github-copilot16
codex16
kimi-cli16