---
title: "Upgrade Guide"
description: "Learn how to migrate between Unhead versions for Vue users."
canonical_url: "https://unhead.unjs.io/docs/vue/head/guides/get-started/migration"
last_updated: "2026-08-04T20:35:31.023Z"
---

## Migrate to v3 (from v2)

Unhead v3 changes several core defaults and removes v2 compatibility behavior. This guide covers the changes relevant to Vue users.

<tip>

Nuxt users should not be affected by most of these changes as Nuxt handles the integration automatically.

</tip>

### Legacy Property Names

🚦 Impact Level: High

The current `createHead()` entry points omit `DeprecationsPlugin`. Rename these properties before upgrading; the deprecated plugin is still available for staged migrations.

**children → innerHTML**

```diff
useHead({
  script: [{
-   children: 'console.log("hello")',
+   innerHTML: 'console.log("hello")',
  }]
})
```

**hid / vmid → key**

```diff
useHead({
  meta: [{
-   hid: 'description',
+   key: 'description',
    name: 'description',
    content: 'My description'
  }]
})
```

**body: true → tagPosition: 'bodyClose'**

```diff
useHead({
  script: [{
    src: '/script.js',
-   body: true,
+   tagPosition: 'bodyClose',
  }]
})
```

### Schema.org Plugin

🚦 Impact Level: High

The `PluginSchemaOrg` and `SchemaOrgUnheadPlugin` exports have been removed. Use `UnheadSchemaOrg` instead.

```diff
- import { PluginSchemaOrg } from '@unhead/schema-org/vue'
+ import { UnheadSchemaOrg } from '@unhead/schema-org/vue'

const head = createHead({
  plugins: [
-   PluginSchemaOrg()
+   UnheadSchemaOrg()
  ]
})
```

### Server Composables Deprecated

🚦 Impact Level: Medium-High

The `useServerHead`, `useServerHeadSafe`, and `useServerSeoMeta` composables remain as deprecated aliases. Use the standard composables instead.

```diff
- import { useServerHead, useServerSeoMeta } from '@unhead/vue'
+ import { useHead, useSeoMeta } from '@unhead/vue'

- useServerHead({ title: 'My Page' })
+ useHead({ title: 'My Page' })

- useServerSeoMeta({ description: 'My description' })
+ useSeoMeta({ description: 'My description' })
```

If you need server-only head management, use conditional logic:

```ts
if (import.meta.server) {
  useHead({ title: 'Server Only' })
}
```

### Vue Legacy Exports Deprecated

🚦 Impact Level: Medium

The `/legacy` export path remains available for migration compatibility and is scheduled for removal in v4. Move to the client or server entry point now:

```diff
- import { createHead } from '@unhead/vue/legacy'
+ import { createHead } from '@unhead/vue/client'
// or for SSR
+ import { createHead } from '@unhead/vue/server'
```

**createHeadCore Removed**

```diff
- import { createHeadCore } from '@unhead/vue'
+ import { createHead } from '@unhead/vue/server'
// or for client
+ import { createHead } from '@unhead/vue/client'
```

### Core API Changes

🚦 Impact Level: Medium

**headEntries() → entries Map**

```diff
- const entries = head.headEntries()
+ const entries = [...head.entries.values()]
```

**mode Option Removed**

The `mode` option on head entries has been removed.

```diff
useHead({
  title: 'My Page',
- }, { mode: 'server' })
+ })
```

### Hooks

🚦 Impact Level: Low

The `init` hook was removed. `dom:renderTag` remains in the type definitions for compatibility but is deprecated and no longer called internally. `dom:rendered` is also deprecated but is still emitted; prefer the `onRendered` entry option for entry-specific work.

The `dom:beforeRender` hook is now synchronous and `renderDOMHead` no longer returns a Promise:

```diff
- await renderDOMHead(head, { document })
+ renderDOMHead(head, { document })
```

The SSR hooks (`ssr:beforeRender`, `ssr:render`, `ssr:rendered`) are now synchronous and `renderSSRHead` no longer returns a Promise:

```diff
- const head = await renderSSRHead(head)
+ const head = renderSSRHead(head)
```

### Other API Changes

- `setHeadInjectionHandler` was removed; head injection is handled automatically.
- `resolveUnrefHeadInput` and `DeprecationsPlugin` remain available as compatibility helpers. Avoid them in new code.

### Type Changes

🚦 Impact Level: Low

```diff
- import type { Head, MetaFlatInput } from '@unhead/vue'
+ import type { HeadTag, MetaFlat } from '@unhead/vue'
```

---

## Migrate to v2 (from v1)

Unhead v2 added adapters for other frameworks. This section covers the v1-to-v2 changes that affect Vue users.

### Client / Server Subpath Exports

🚦 Impact Level: Critical

<tip>

Nuxt should not be affected by this change.

</tip>

**⚠️ Breaking Changes:**

- `createServerHead()` and `createHead()` exports from `unhead` are removed

The path where you import `createHead` from has been updated to be a subpath export.

**Client bundle:**

```diff
-import { createHead } from '@unhead/vue'
+import { createHead } from '@unhead/vue/client'
import { createApp } from 'vue'

const app = createApp()
const head = createHead()
app.use(head)
```

**Server bundle:**

```diff
-import { createServerHead } from '@unhead/vue'
+import { createHead } from '@unhead/vue/server'
import { createApp } from 'vue'

const app = createApp()

-const head = createServerHead()
+const head = createHead()

app.use(head)
```

Create one client head in the browser, but a fresh server head for every request. A shared SSR application factory must not reuse a head from `@unhead/vue/client`; that constructor omits server defaults, Vue server resolvers, and payload support.

### Remove Legacy Instance Annotations

V1 examples sometimes annotated the instance with `VueHeadClient<MergeHead>`. These types no longer describe the v2 factory. Remove the annotation and let `createHead()` infer its result:

```diff
-import type { MergeHead, VueHeadClient } from '@unhead/vue'
 import { createHead } from '@unhead/vue/client'

-const head = createHead() as unknown as VueHeadClient<MergeHead>
+const head = createHead()
```

### Replace Legacy Alias Packages

Move direct imports from the compatibility packages to their v2 entry points:

<table>
<thead>
  <tr>
    <th>
      V1 package
    </th>
    
    <th>
      V2 entry point
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        @unhead/ssr
      </code>
    </td>
    
    <td>
      <code>
        unhead/server
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @unhead/dom
      </code>
    </td>
    
    <td>
      <code>
        unhead/client
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @unhead/shared
      </code>
    </td>
    
    <td>
      <code>
        unhead/utils
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @unhead/schema
      </code>
    </td>
    
    <td>
      <code>
        unhead/types
      </code>
    </td>
  </tr>
</tbody>
</table>

### Removed Implicit Context

🚦 Impact Level: Critical

<tip>

Nuxt should not be affected by this change.

</tip>

**⚠️ Breaking Changes:**

- `setHeadInjectionHandler()` is removed
- Error may be thrown when using `useHead()` after async operations

The implicit context implementation kept a global instance of Unhead available so that you could use the `useHead()` composables anywhere in your application.

In v2, the core composables no longer have access to the Unhead instance. Instead, you must pass the Unhead instance to the composables if you'd like to use Unhead in a non-Vue context.

```vue [Vue Context Lost]
<script setup lang="ts">
// In Vue this happens in lifecycle hooks where we have async operations.
onMounted(async () => {
  await fetchSomeData()
  useHead({
    title: 'This will not work'
  })
})
</script>
```

If you're getting errors on your `useHead()` about context, check the [Reactivity and Async Context](/docs/vue/head/guides/core-concepts/reactivity-and-context) guide.

### Removed `vmid`, `hid`, `children`, `body`

🚦 Impact Level: High

Unhead v1 accepted the Vue Meta properties `vmid`, `hid`, `children`, and `body`.

You must update these properties to the appropriate replacement or remove them. See the [v3 migration section](#legacy-property-names) for the replacements.

### Opt-in Template Params & Tag Alias Sorting

🚦 Impact Level: High

Template parameters and tag alias sorting now require optional plugins:

```ts
import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins'

createHead({
  plugins: [TemplateParamsPlugin, AliasSortingPlugin]
})
```

### Vue 2 Support

🚦 Impact Level: Critical

Unhead v3 no longer supports Vue v2. If you're using Vue v2, you will need to lock your dependencies to the latest v1 version of Unhead.

### Promise Input Support

🚦 Impact Level: Medium

Promise inputs are no longer resolved by core. Prefer awaiting them before passing the result to Unhead, or register the optional plugin when that is not possible:

```ts
import { createHead } from '@unhead/vue/client'
import { PromisesPlugin } from '@unhead/vue/plugins'

const unhead = createHead({
  plugins: [PromisesPlugin]
})
```

The plugin resolves values outside the synchronous tag pipeline. Pending entries are omitted from the current render, then rendered automatically on the client after resolving. Resolve inputs before SSR when they must appear in its first render.

### Updated `useScript()`

🚦 Impact Level: High

**⚠️ Breaking Changes:**

- Script instance is no longer augmented as a proxy and promise
- `script.proxy` is rewritten for simpler, more stable behavior
- `stub()` and runtime hook `script:instance-fn` are removed

#### Replacing Promise Usage

```diff
const script = useScript()

-script.then(() => console.log('loaded')
+script.onLoaded(() => console.log('loaded'))
```

#### Replacing Proxy Usage

```diff
const script = useScript('..', {
  use() { return { foo: [] } }
})

-script.foo.push('bar')
+script.proxy.foo.push('bar')
```

### Tag Sorting Updated

🚦 Impact Level: Low

[Capo.js](https://rviscomi.github.io/capo.js/) sorting is now the default. You can opt-out:

```ts
createHead({
  disableCapoSorting: true,
})
```

### Default SSR Tags

🚦 Impact Level: Low

During SSR, Unhead now inserts these default tags:

- `<meta charset="utf-8">`
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
- `<html lang="en">`

```ts
import { createHead } from '@unhead/vue/server'

// disable when creating the head instance
createHead({
  disableDefaults: true,
})
```

Or override the defaults:

```ts
import { useHead } from '@unhead/vue'

useHead({
  htmlAttrs: {
    lang: 'fr'
  }
})
```

### CJS Exports Removed

🚦 Impact Level: Low

CommonJS exports have been removed in favor of ESM only.

```diff
-const { useHead } = require('@unhead/vue')
+import { useHead } from '@unhead/vue'
```

### Deprecated `@unhead/schema`

🚦 Impact Level: Low

The `@unhead/schema` package is deprecated. Import from `@unhead/vue/types` instead.

```diff
-import { HeadTag } from '@unhead/schema'
+import { HeadTag } from '@unhead/vue/types'
```
