unhead-vue-skilld
Full-stack <head> manager for Vue (@unhead/vue 3.x). Use when writing, reviewing, or debugging code that imports @unhead/vue, calls useHead, useSeoMeta, useHeadSafe, useScript, injectHead, createHead, or sets up Unhead SSR / streaming SSR / build plugins in a Vue app.
How do I install this agent skill?
npx skills add https://github.com/skilld-dev/vue-ecosystem-skills --skill unhead-vue-skilldIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The unhead-vue-skilld skill is an extensive documentation resource for the Unhead library, focusing on Vue integration for managing document head tags. It is safe for use and follows standard development and documentation practices. The analysis identifies a potential indirect prompt injection surface where user-provided data could lead to Cross-Site Scripting (XSS) if rendered unsafely. However, the documentation correctly identifies this risk and provides a security-focused 'useHeadSafe' composable specifically designed to mitigate such issues.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
@unhead/vue 3.4.1
Full-stack <head> management for Vue 3. ESM only ("type": "module"), no CJS.
Peer deps: vue >= 3.5.18; optional vite >= 6.4.2, webpack >= 5.0.0 (source: package.json:138-150).
Vue 2: not supported; use @unhead/vue@^1.
Nuxt: Unhead is integrated already; do not install or set it up manually.
Setup (mandatory)
Install @unhead/vue, create the head instance, and register it as a Vue plugin. createHead comes from a subpath export, not the package root.
// entry-client.ts (SPA or hydration)
import { createHead } from '@unhead/vue/client'
import { createApp } from './main'
const { app } = createApp()
const head = createHead()
app.use(head)
app.mount('#app')
// entry-server.ts (SSR)
import { createHead } from '@unhead/vue/server'
import { renderToString } from 'vue/server-renderer'
import { createApp } from './main'
export async function render(url: string) {
const { app } = createApp()
const head = createHead()
app.use(head)
const html = await renderToString(app)
return { html, head } // render tags later with transformHtmlTemplate(head, template)
}
Full SSR template wiring, streaming SSR, Options API, and auto-imports: references/setup.md.
Composables
All imported from @unhead/vue root and used inside setup():
<script setup lang="ts">
import { useHead, useSeoMeta } from '@unhead/vue'
useHead({
title: () => `${user.value.name} - Profile`, // getters, refs, computed all reactive
titleTemplate: '%s | My Site',
meta: [{ name: 'description', content: () => description.value }],
})
useSeoMeta({ // flat, type-safe keys; og/twitter handled automatically
ogTitle: 'My Page',
ogImage: 'https://example.com/image.jpg',
})
</script>
// Third-party scripts
import { useScript } from '@unhead/vue'
const { proxy, onLoaded, status } = useScript(
{ src: 'https://example.com/analytics.js' },
{ use: () => window.analytics, trigger: 'client' },
)
proxy.track('pageview') // queued until the script loads
Details, options tables, and recipes: references/composables.md.
Critical rules
- Async context: calling
useHead()after anawaitinsideonMounted()/watchers throwsuseHead() was called without provide context. Fix, in order of preference: top-levelawaitin<script setup>; declareuseHead()once with refs/computed and update the ref later; captureconst head = injectHead()at setup time and passuseHead(input, { head }). Source: https://unhead.unjs.io/docs/vue/head/guides/core-concepts/reactivity-and-context - Never call
useHead()inside a watcher callback: each call pushes a new entry and duplicates tags. Pass the reactive value into oneuseHead()call instead. - Deduplication key is
key, nothid/vmid:meta: [{ key: 'description', name: 'description', content: '...' }]. - Inline content:
innerHTML(waschildrenin v1); body scripts usetagPosition: 'bodyClose'(wasbody: true). - Untrusted input: use
useHeadSafe(); it whitelists attributes and strips scripts/handlers. - Head instance access in Vue: use
injectHead()from@unhead/vue, notgetActiveHead()fromunhead; only the former binds to component context (e.g.onServerPrefetch).
Deprecated in 3.4.1 (present but slated for removal)
| Export | Status | Replacement |
|---|---|---|
useServerHead, useServerHeadSafe, useServerSeoMeta | deprecated (dist/index.d.ts:26-30) | useHead / useHeadSafe / useSeoMeta |
@unhead/vue/legacy (createHead, createServerHead, legacyPlugins) | deprecated, removed in v4 (dist/legacy.d.ts:14-30) | @unhead/vue/client / @unhead/vue/server |
resolveUnrefHeadInput | deprecated (dist/utils.d.ts:9) | resolveTags(head) from unhead/utils |
unheadVuePlugin from @unhead/vue/stream/vite | deprecated (dist/stream/vite.d.ts:8) | Unhead({ streaming: true }).vite() from @unhead/vue/bundler |
MergeHead type | deprecated (dist/types.d.ts:66) | generics on VueHeadClient |
Removed in v3 (will not compile): DeprecationsPlugin, setHeadInjectionHandler, createHeadCore, { mode: 'server' | 'client' } entry option, init/dom:renderTag/dom:rendered hooks, headEntries() (use [...head.entries.values()]).
Migration details: references/migration.md.
Export map
| Subpath | Purpose |
|---|---|
@unhead/vue | composables: useHead, useSeoMeta, useHeadSafe, useScript, injectHead, VueHeadMixin, unheadVueComposablesImports, createUnhead, defineLink, defineScript |
@unhead/vue/client | createHead() (SPA/hydration), renderDOMHead, VueHeadMixin |
@unhead/vue/server | createHead() (SSR), renderSSRHead, transformHtmlTemplate, propsToString |
@unhead/vue/components | <Head> component |
@unhead/vue/plugins | re-exports unhead/plugins (TemplateParamsPlugin, AliasSortingPlugin, PromisePlugin, InferSeoMetaPlugin, ValidatePlugin, ...) |
@unhead/vue/bundler | unified build plugin Unhead() for vite/webpack/rspack/rollup |
@unhead/vue/vite | Vite-only Unhead(); prefer /bundler |
@unhead/vue/stream/server, /stream/client | createStreamableHead() for streaming SSR |
@unhead/vue/types, /utils, /scripts, /legacy | types, utilities, script types, legacy shims |
Signatures and full type surface: references/api-surface.md.
Optional plugins at creation
Template params (%s, %separator), alias sorting (before:/after:), and Promise inputs are opt-in since v2:
import { createHead } from '@unhead/vue/client'
import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins'
const head = createHead({ plugins: [TemplateParamsPlugin, AliasSortingPlugin] })
Without TemplateParamsPlugin, %siteName placeholders render literally.
Build plugin (recommended for standalone Vue apps)
// vite.config.ts
import { Unhead } from '@unhead/vue/bundler'
export default defineConfig({
plugins: [vue(), Unhead()], // add { streaming: true } for streaming SSR
})
Tree-shakes server-only composables from the client bundle, converts useSeoMeta() to useHead() (~3kb), injects ValidatePlugin in dev. Options table and webpack notes: references/build-plugins.md.
Common mistakes
- Importing
createHeadfrom@unhead/vueroot (removed in v2): use/clientor/server. - Awaiting
renderSSRHead/renderDOMHead: both are synchronous in v3. useScript()return is not a Promise: useonLoaded(() => ...); call APIs through.proxy.- Meta tags require
content:{ name: 'description', content: null }removes the tag. - Font preloads require
crossorigin:{ rel: 'preload', as: 'font', href: '/f.woff2', crossorigin: 'anonymous' }.
References
- references/setup.md — SPA, SSR, streaming SSR, Options API,
<Head>component, auto-imports, defaults - references/composables.md —
useHead,useSeoMeta,useHeadSafe,useScriptusage and options - references/api-surface.md — export map, types, instance API
- references/build-plugins.md —
Unhead()bundler plugin and streaming - references/migration.md — v2 and v3 breaking changes
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/skilld-dev/vue-ecosystem-skills/unhead-vue-skilld">View unhead-vue-skilld on skillZs</a>