This section documents version-specific API changes — prioritize recent major/minor releases.
-
BREAKING: renderDOMHead() and renderSSRHead() now synchronous — remove await keyword, these functions no longer return promises source
-
BREAKING: children property deprecated — replace with innerHTML in script/style tags to set inline content source
-
BREAKING: hid / vmid property deprecated — replace with key property to uniquely identify meta tags source
-
BREAKING: body: true property deprecated — replace with tagPosition: 'bodyClose' to inject scripts at end of body source
-
BREAKING: useServerHead(), useServerHeadSafe(), useServerSeoMeta() removed — use useHead(), useHeadSafe(), useSeoMeta() instead, or wrap in if (import.meta.server) for server-only logic source
-
BREAKING: createHeadCore() removed — import createHead from @unhead/vue/server or @unhead/vue/client depending on context source
-
BREAKING: @unhead/vue/legacy export path removed — use /client or /server subpath imports explicitly source
-
BREAKING: Type Head renamed to HeadTag — update imports and type annotations to use new name source
-
BREAKING: CommonJS removed, ESM-only — all packages must be imported via ESM (import syntax), no more require() source
-
BREAKING: TemplateParamsPlugin and AliasSortingPlugin now opt-in — import and register explicitly if needed for template variable substitution and tag alias sorting source
-
BREAKING: init hook removed — no longer available in hook lifecycle source
-
BREAKING: Vite plugin import changed — use named Unhead export from @unhead/vue/vite instead of default export from @unhead/addons/vite source
-
BREAKING: headEntries() method deprecated — access entries via head.entries Map instead: [...head.entries.values()] source
-
BREAKING: resolveUnrefHeadInput() removed — reactive resolution now happens automatically during useHead() calls source
-
BREAKING: Link/Script/Meta types now strict — union types no longer fall back to generic GenericLink/GenericScript, enforce per-tag constraints (e.g. preload requires as attribute), custom rel/type need satisfies assertion source
-
BREAKING: dom:renderTag, dom:rendered hooks deprecated — use onRendered option on useHead() instead for accessing resolved head after DOM update source
-
BREAKING: mode option on head entries removed — use client/server createHead imports to control entry scope source
-
NEW: onRendered callback option — pass callback to useHead() to access resolved head entry after DOM updates: useHead({...}, { onRendered: () => {...} }) source
-
NEW: tagWeight option on createHead() — override default CAPO tag weight function for custom tag sorting during rendering source
-
NEW: ValidatePlugin — inspects resolved head output and warns about common mistakes (missing titles, duplicate meta tags, preload priority conflicts, late charset, render-blocking scripts, excessive fetchpriority, preconnect without crossorigin) and v2 migration issues, auto-injected in dev via Vite plugin source
-
NEW: CanonicalPlugin — auto-generates <link rel="canonical"> tags, resolves relative URLs to absolute in og:image/twitter:image/og:url, filters query parameters (strips utm_source, fbclid, gclid by default), normalizes trailing slashes, strips hash fragments source
-
NEW: MinifyPlugin — minifies inline <script> and <style> tag content during SSR using lightweight pure-JS minifiers, safe for edge/serverless source
-
NEW: createStreamableHead() — enables streaming SSR support, head tags update dynamically as suspense boundaries resolve during streaming, server-side collects entries in queue stub at window.__unhead__ source
-
NEW: useHead() type narrowing — types now narrow based on input, link/script/meta tags resolve to specific subtypes (e.g. StylesheetLink, PreloadLink, ModuleScript, JsonLdScript) instead of generic union for precise autocomplete and type errors source
-
NEW: Vite DevTools integration — unified Unhead() plugin from @unhead/vue/vite exposes DevTools panel with live head state, source file/line tracing, SEO overview, useScript() load status, active plugins, template params, and validation warnings source
-
Use reactive state (refs/computed) directly in useHead() rather than calling useHead() in watchers — changes update automatically without creating multiple entries source
-
Call injectHead() at component setup time before async operations to maintain Vue's provide/inject context for later head updates source
-
Use top-level await in script setup or effectScope() to preserve async context — Vue's compile-time transforms handle context automatically at the top level source
-
Use useScript() with warmupStrategy for third-party scripts — preconnect for CDN connections, preload for immediately needed scripts, prefetch for scripts needed soon source
-
Wrap script initialization in reusable composables and rely on the singleton pattern — scripts with the same source are loaded once globally regardless of component calls source
-
Set tagPosition: 'bodyClose' for non-critical scripts like analytics to avoid blocking initial render source
-
Use tagPriority aliases ('critical', 'high', 'low') instead of numeric weights to maintain performance-optimized Capo.js ordering source
-
Use useSeoMeta() for SEO tags instead of useHead() — provides full TypeScript support and automatically handles name vs property attributes source
-
Enable CanonicalPlugin({ canonicalHost: 'https://mysite.com' }) to auto-convert relative URLs to absolute in og:image, twitter:image, and canonical links — required for proper social sharing and SEO source
-
Use InferSeoMetaPlugin() to automatically generate og:title, og:description, and twitter:card from existing title and description — reduces duplicate meta tag definitions source
-
Enable ValidatePlugin({ root: process.cwd() }) in development to catch head tag mistakes like non-absolute URLs, missing OG companions, and typos in meta properties source
-
Use useHeadSafe() when working with user-generated content or third-party input — provides XSS protection via a strict whitelist of allowed tags and attributes source
-
Pause DOM updates during route transitions by hooking dom:beforeRender and calling renderDOMHead(head) after navigation completes — prevents jarring tag updates with Suspense components source