All skills
skilld-dev avatar

/unhead-vue-skilld

@b9c39cb

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.

Use this Skill: https://skilld.dev/gh/skilld-dev/vue-ecosystem-skills/unhead-vue-skilld

This session only. Nothing lands on disk.

referencessetup.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Setup guide

All examples verified against @unhead/vue 3.4.1 (dist/client.d.ts, dist/server.d.ts, dist/stream/server.d.ts).

SPA / client

// entry-client.ts
import { createHead } from '@unhead/vue/client'
import { createApp } from './main'

const { app } = createApp()
const head = createHead()
app.use(head)
app.mount('#app')

createHead(options?: CreateClientHeadOptions): VueHeadClient from @unhead/vue/client (dist/client.d.ts:10). DOM updates are debounced to the next tick internally (dist/client.mjs debouncedRenderer).

SSR

Server entry creates the instance, the HTTP layer renders tags into the HTML template after Vue finishes:

// entry-server.ts
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 }
}
// server.ts (Node/Express layer)
import { transformHtmlTemplate } from '@unhead/vue/server'

const html = transformHtmlTemplate(
  rendered.head,
  template.replace('<!--app-html-->', rendered.html ?? ''),
)
res.status(200).set({ 'Content-Type': 'text/html' }).send(html)

@unhead/vue/server also re-exports renderSSRHead, prepareTemplate, propsToString, PreparedTemplate, SSRHeadPayload (dist/server.d.ts:5). In v3 renderSSRHead and transformHtmlTemplate are synchronous. The server instance registers the VueResolver prop resolver automatically (dist/server.mjs).

Source: https://unhead.unjs.io/docs/vue/head/guides/get-started/installation

Default tags

Unhead inserts <meta charset="utf-8">, <meta name="viewport" content="width=device-width, initial-scale=1">, and <html lang="en">. Override via init, disable via createHead({ disableDefaults: true }):

const head = createHead({
  init: [{ title: 'Default title', titleTemplate: '%s | My Site', htmlAttrs: { lang: 'fr' } }],
})

Streaming SSR

For apps with async components behind Suspense. Vue emits resolved Suspense boundaries in document order, so wrapStream() writes each head patch after its app chunk (dist/stream/server.d.ts:28-33).

// vite.config.ts
import { Unhead } from '@unhead/vue/vite'
export default defineConfig({ plugins: [vue(), Unhead({ streaming: true })] })
// entry-server.ts
import { renderToWebStream } from 'vue/server-renderer'
import { createStreamableHead } from '@unhead/vue/stream/server'
import { VueHeadMixin } from '@unhead/vue'

export async function render(url: string, template: string) {
  const { app, router } = createApp()
  const { head, wrapStream } = createStreamableHead()
  app.use(head)
  app.mixin(VueHeadMixin) // required with streaming so async components register head
  router.push(url)
  await router.isReady()
  return wrapStream(renderToWebStream(app), template)
}
// entry-client.ts
import { createStreamableHead } from '@unhead/vue/stream/client'
const head = createStreamableHead()
app.use(head)

Skip streaming when no async components use useHead(); it adds complexity for no benefit. Source: https://unhead.unjs.io/docs/vue/head/guides/core-concepts/streaming

<Head> component

Import from @unhead/vue/components. In 3.4.1 it is a single Head component that converts child vnodes into a head entry (dist/components.mjs vnodesToHeadObj); use plain HTML elements as children:

<script setup lang="ts">
import { Head } from '@unhead/vue/components'
</script>

<template>
  <Head>
    <title>My awesome site</title>
    <meta name="description" content="My awesome site description">
  </Head>
</template>

Child vnodes map to entry keys: html -> htmlAttrs, body -> bodyAttrs, others by tag name (title, meta, link, script, style, noscript, base). Tag children become textContent, except script children which become innerHTML. Tags are removed when the component unmounts. Prefer useHead() for full TypeScript support. Source: https://unhead.unjs.io/docs/vue/head/guides/core-concepts/components

Options API

Install VueHeadMixin globally (exported from root, /client, and /server):

import { createHead, VueHeadMixin } from '@unhead/vue/client'
const head = createHead()
app.use(head)
app.mixin(VueHeadMixin)
<script>
export default {
  data: () => ({ title: 'Hello World' }),
  head() {
    return { title: this.title } // reactive on this; plain object also allowed
  },
}
</script>

Source: https://unhead.unjs.io/docs/vue/head/guides/core-concepts/options-api

Auto-imports (unplugin-auto-import)

// vite.config.ts
import { unheadVueComposablesImports } from '@unhead/vue'
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [AutoImport({ imports: [unheadVueComposablesImports] })],
})

unheadVueComposablesImports covers injectHead, useHead, useSeoMeta, useHeadSafe (dist/index.d.ts:15).

Pausing DOM rendering

Useful with <Suspense> and route transitions: gate dom:beforeRender on a flag, then call renderDOMHead(head) (synchronous in v3) to flush.

import { createHead } from '@unhead/vue/client'
import { renderDOMHead } from 'unhead/client'

const head = createHead()
let pauseDOMUpdates = true
head.hooks.hook('dom:beforeRender', (ctx) => { ctx.shouldRender = !pauseDOMUpdates })

router.beforeEach(() => { pauseDOMUpdates = true })
router.afterEach(() => {
  if (pauseDOMUpdates) {
    pauseDOMUpdates = false
    renderDOMHead(head)
  }
})

Source: https://unhead.unjs.io/docs/vue/head/guides/core-concepts/pausing-dom-rendering

Source: SKILL.md on GitHub

No alerts8d3 checks · Risk SAFE
  • Gen Agent Trust Hub8d

    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.

  • Socket8d

    No alerts

  • Snyk9d

    Risk: LOW · No issues

Signed by skilld at b9c39cb. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last week.

Activeupdated last week

README badge

README badge for skilld-dev/vue-ecosystem-skills/unhead-vue-skilld