---
title: "Migrating from vue-meta to Unhead · Nuxt SEO"
canonical_url: "https://nuxtseo.com/learn-seo/vue/mastering-meta/migrating-vue-meta"
last_updated: "2026-07-16T12:00:00.000Z"
meta:
  author: "Harlan Wilton"
  description: "Migrate from vue-meta to Unhead in Vue 3, with syntax mapping, breaking changes, and search-and-replace patterns."
  "og:description": "Migrate from vue-meta to Unhead in Vue 3, with syntax mapping, breaking changes, and search-and-replace patterns."
  "og:title": "Migrating from vue-meta to Unhead · Nuxt SEO"
---

Nuxt SEO on GitHub

# **Migrating from vue-meta to Unhead**

Migrate from vue-meta to Unhead in Vue 3, with syntax mapping, breaking changes, and search-and-replace patterns.

[Harlan Wilton](https://x.com/harlan-zw)6 mins read Published **Dec 17, 2025** Updated **Jul 16, 2026**

**What you'll learn**

- vue-meta never shipped Vue 3 support and was archived in 2025
- Unhead is the modern replacement with full Vue 3 support
- Most APIs translate directly: `**metaInfo**` becomes `**useHead**`/ `**useSeoMeta**`

vue-meta was the standard for Vue 2 head management but never shipped a stable Vue 3 version: it stalled at a `**3.0.0-alpha**` release. The [**~~repository was archived~~**](https://github.com/nuxt/vue-meta) in October 2025 after years without updates. [**~~Unhead~~**](https://unhead.unjs.io/) is its modern replacement, built by the same ecosystem and Vue 3 native from the start.

## Quick Comparison

```ts
export default {
  metaInfo() {
    return {
      title: 'My Page',
      meta: [
        { name: 'description', content: 'Page description' }
      ]
    }
  }
}
```

Unhead's `**useSeoMeta()**` flattens the nested structure. No more `**meta**` arrays with `**name**`/`**content**` objects.

## Syntax Mapping

| **vue-meta** | **Unhead** | **Notes** |
| --- | --- | --- |
| `**metaInfo: {}**` | `**useHead({})**` | Static object |
| `**metaInfo()**` | `**useHead({})**` with refs | Reactive by default |
| `**title**` | `**title**` | Same |
| `**titleTemplate: '%s - Site'**` | `**titleTemplate: '%s - Site'**` | Same |
| `**meta: [{ name, content }]**` | `**useSeoMeta({ name: value })**` | Flattened |
| `**vmid**` / `**hid**` | `**key**` | For deduplication |
| `**children**` | `**innerHTML**` | Script content |
| `**body: true**` | `**tagPosition: 'bodyClose'**` | Script positioning |

## Migration Steps

### 1. Remove vue-meta

```bash
npm uninstall vue-meta
npm install @unhead/vue
```

### 2. Update Plugin Setup

```ts
import Vue from 'vue'
import VueMeta from 'vue-meta'

Vue.use(VueMeta)
```

For SSR apps, import from `**@unhead/vue/server**` instead:

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

### 3. Convert Components

```vue
<script lang="ts">
export default {
  data() {
    return { pageTitle: 'About Us' }
  },
  metaInfo() {
    return {
      title: this.pageTitle,
      titleTemplate: '%s | MySite',
      meta: [
        { name: 'description', content: 'About our company' },
        { property: 'og:title', content: this.pageTitle },
        { property: 'og:description', content: 'About our company' }
      ]
    }
  }
}
</script>
```

### 4. Search and Replace Patterns

| **Find** | **Replace** |
| --- | --- |
| `**metaInfo()**` or `**metaInfo: {**` | `**useHead({**` or `**useSeoMeta({**` |
| `**this.$meta().refresh()**` | Remove (automatic) |
| `**vmid:**` | `**key:**` |
| `**hid:**` | `**key:**` |
| `**{ name: 'description', content:**` | `**description:**` (in useSeoMeta) |
| `**{ property: 'og:title', content:**` | `**ogTitle:**` (in useSeoMeta) |

## Breaking Changes

### Reactivity Model

vue-meta required `**metaInfo()**` as a function for reactivity; Unhead is reactive by default, so pass refs directly:

```vue
<script lang="ts">
// vue-meta: function required for reactivity
export default {
  metaInfo() {
    return { title: this.dynamicTitle }
  }
}
</script>
```

### No Implicit Context After Async

Unhead v2 removed implicit context. Don't call `**useHead()**` after `**await**` without saving the head instance:

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

// May fail in Unhead v2
async function loadData() {
  const data = await fetchData()
  useHead({ title: data.title }) // Context lost
}
```

### SSR Rendering

vue-meta had `**inject()**` for SSR. Unhead uses `**renderSSRHead()**`:

```ts
// Server entry
import { renderSSRHead } from '@unhead/ssr'

const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(head)
```

### Template Params Plugin

`**titleTemplate**` params like `**%separator**` require explicit plugin registration in Unhead v2:

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

const head = createHead({
  plugins: [TemplateParamsPlugin()]
})
```

## Why Migrate?

| **vue-meta** | **Unhead** |
| --- | --- |
| Last stable release: 2020 | Actively maintained |
| Vue 2 only | Vue 3 native |
| Limited TypeScript | Full type safety |
| Community abandoned | Official Nuxt ecosystem |

Unhead also provides `**useSeoMeta()**` with autocomplete for all SEO properties, so there's no more guessing meta tag names.

If you're using Nuxt, you don't need to install Unhead separately. It's built in. See [**~~Nuxt SEO~~**](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction) for Nuxt-specific setup.

## Checklist

**Checklist**

- Uninstalled `**vue-meta**` and installed `**@unhead/vue**`
- Replaced `**Vue.use(VueMeta)**` with `**createHead()**` and `**app.use(head)**`
- Converted every `**metaInfo()**` block to `**useHead()**` or `**useSeoMeta()**`
- Renamed `**vmid**`/ `**hid**` fields to `**key**`
- Pass refs and computed getters directly to `**useHead()**`, not `**.value**`
- Call `**useHead()**` before an `**await**`, or use `**injectHead()**` to keep context after async code
- Registered `**TemplateParamsPlugin()**` if you use custom `**titleTemplate**` params like `**%separator**`

[**The 2026 SEO Checklist for Nuxt & Vue ** Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.](https://nuxtseo.com/learn-seo/checklist) [Haven't launched yet? Start with the **Pre-Launch Warmup**](https://nuxtseo.com/learn-seo/pre-launch-warmup)

---

### **Related **

[**Page Titles in Vue**](https://nuxtseo.com/learn-seo/vue/mastering-meta/titles)

[**Meta Descriptions**](https://nuxtseo.com/learn-seo/vue/mastering-meta/descriptions)

[**Social Sharing Tags**](https://nuxtseo.com/learn-seo/vue/mastering-meta/social-sharing)

### **Open Source **

[**unjs/unhead**** Public **](https://github.com/unjs/unhead)

[**Schema.org** Add type-safe JSON-LD to a Vue app with @unhead/schema-org's useSchemaOrg, no Nuxt required.](https://nuxtseo.com/learn-seo/vue/mastering-meta/schema-org) [**Rich Results** Google removed FAQ and HowTo snippets and cut more types in 2026. See which Schema.org types still earn rich results and how to add them in Vue.](https://nuxtseo.com/learn-seo/vue/mastering-meta/rich-results)

**On this page**

- [Quick Comparison](#quick-comparison)
- [Syntax Mapping](#syntax-mapping)
- [Migration Steps](#migration-steps)
- [Breaking Changes](#breaking-changes)
- [Why Migrate?](#why-migrate)
- [Checklist](#checklist)