---
title: "Installing Unhead with Vue"
description: "Set up Unhead in Vue 3 with createHead() and app.use(), including client and server entry points."
canonical_url: "https://unhead.unjs.io/docs/vue/head/guides/get-started/installation"
last_updated: "2026-07-24T23:17:09.587Z"
---

Install `@unhead/vue`, create a head with `createHead()`, and register it through `app.use(head)`. SSR uses separate client and server entry points with `transformHtmlTemplate()`.

Unhead's Vue adapter originated in the [@vueuse/head](https://github.com/vueuse/head) repository, which provided a Vue 3 successor to [Vue Meta](https://github.com/nuxt/vue-meta).

The examples follow [Vite's SSR structure](https://vite.dev/guide/ssr.html). A Vue SPA uses the same client entry.

<note>

Using [Nuxt](https://nuxt.com/docs/4.x/getting-started/seo-meta)? Unhead is already integrated, and you can skip this guide.

</note>

## Demos

- [StackBlitz - Unhead - Vite + Vue SSR](https://stackblitz.com/edit/github-1ftqrmwn)
- [StackBlitz - Unhead - Vue SPA](https://stackblitz.com/edit/vitejs-vite-9ztda642)

## Setup

### 1. Add Dependency

Install the `@unhead/vue` dependency in your project.

<module-install name="@unhead/vue@next">



</module-install>

#### Vue 2

Unhead v2 and later do not support Vue 2. Vue 2 projects need `@unhead/vue@^1`.

<module-install name="@unhead/vue@^1">



</module-install>

Then follow the [v1 Vue installation guide](https://v1.unhead.unjs.io/setup/vue/installation).

### 2. Set Up Client-Side Rendering

Create the browser instance from `@unhead/vue/client`. In a Vite SSR app this usually belongs in `entry-client.ts`; in an SPA, put it in the main entry.

```ts [src/entry-client.ts]
import './style.css'
import { createApp } from './main'
import { createHead } from '@unhead/vue/client'

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

app.mount('#app')
```

### 3. Set Up Server-Side Rendering

<note>

Serving your app as an SPA? You can [skip](#4-server-defaults) this step.

</note>

For SSR, create a fresh instance from `@unhead/vue/server` for each request and return it with the rendered app.

```ts [src/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 ctx = {}
  const html = await renderToString(app, ctx)

  return { html, head }
}
```

After Vue renders, pass the instance and complete HTML template to `transformHtmlTemplate()`:

```ts [server.ts]
import { transformHtmlTemplate } from '@unhead/vue/server'
// ...

// Serve HTML
app.use('*all', async (req, res) => {
  try {
    // ...

    const rendered = await render(url)

    const html = transformHtmlTemplate(
      rendered.head,
      template.replace(`<!--app-html-->`, rendered.html ?? '')
    )

    res.status(200).set({ 'Content-Type': 'text/html' }).send(html)
  }
  catch (e) {
    // ...
  }
})
// ..
```

### 4. Server Defaults

On the server, Unhead inserts these default tags:

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

Set server-only defaults through `init`. Keeping them in the server entry also keeps them out of the client bundle.

```ts [src/entry-server.ts]
import { createHead } from '@unhead/vue/server'

export async function render(_url: string) {
  // ...
  const head = createHead({
    init: [
      // change default initial lang
      {
        title: 'Default title',
        titleTemplate: '%s | My Site',
        htmlAttrs: { lang: 'fr' }
      },
    ]
  })
  // ...
}
```

`bodyAttrs` can set attributes or inline styles on `<body>`:

```vue [src/App.vue]
<script setup lang="ts">
import { useHead } from '@unhead/vue'

useHead({
  bodyAttrs: {
    style: 'background: salmon; color: cyan;'
  },
})
</script>
```

### 5. Enable Auto-Imports

[unplugin-auto-import](https://github.com/unplugin/unplugin-auto-import) can provide the composables without explicit imports:

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

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

## Next Steps

Composables:

- [`useHead()`](/docs/head/api/composables/use-head)
- [`useSeoMeta()`](/docs/head/api/composables/use-seo-meta)

Optional integrations:

- Add support for the [Options API](/docs/vue/head/guides/core-concepts/options-api)
- Set up a [`<Head>` component](/docs/vue/head/guides/core-concepts/components)
- Add [`useSchemaOrg()`](/docs/schema-org/api/composables/use-schema-org) for structured data
- Use [`useScript()`](/docs/head/api/composables/use-script) for loading triggers and script lifecycle hooks
