---
title: "Vue Vapor Mode"
description: "Use Unhead composables in Vue Vapor components. Understand lifecycle cleanup, KeepAlive limits, and script ownership."
canonical_url: "https://unhead.unjs.io/docs/vue/head/guides/core-concepts/vapor"
---

> Index of every page on this site: [llms.txt](https://unhead.unjs.io/llms.txt)

Use the Vue composables inside Vapor component setup, with the same head instance installed on your app.
Check the lifecycle limits below before converting components that own page metadata.

This guide describes behavior checked with Vue `3.6.0-rc.11`.
The cleanup behavior requires the changes in [head cleanup #1020](https://github.com/unjs/unhead/pull/1020) and [script cleanup #1021](https://github.com/unjs/unhead/pull/1021).
Check those changes against your installed Unhead version. A passing fixture does not establish support in an earlier release.

## Setup and reactive metadata

Follow the [Vue installation guide](https://unhead.unjs.io/docs/vue/head/guides/get-started/installation) to install `@unhead/vue` and register `createHead()` through `app.use(head)`.
Use a Vue compiler that supports the `vapor` attribute.
If your app uses `createApp()`, install Vue's `vaporInteropPlugin` to render Vapor components inside it.
See Vue's [Vapor setup and compatibility reference](https://github.com/vuejs/core/releases/tag/v3.6.0-rc.1#about-vapor-mode) for app configuration.

Create head entries synchronously during setup:

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

const props = defineProps<{ title: string }>()

useHead({
  title: () => props.title,
  meta: [{ name: 'robots', content: 'noindex' }],
})
</script>

<template>
  <p>{{ title }}</p>
</template>
```

When the parent changes `title`, Unhead updates the document title.
With the head cleanup change, unmounting this component removes its head entry, including the robots tag.
Any remaining parent head entry can then supply the title.

`useSeoMeta()` and `useHeadSafe()` use the same head lifecycle path.
The dedicated Vapor fixture directly exercises `useHead()`.

## Lifecycle limits

Vue's public `getCurrentInstance()` returns `null` inside Vapor components.
Unhead uses the active effect scope for unmount cleanup when no VDOM component instance exists.
See Vue's [feature compatibility list](https://github.com/vuejs/core/releases/tag/v3.6.0-rc.1#feature-compatibility) for instance-dependent APIs.

<table>
<thead>
  <tr>
    <th>
      Situation
    </th>
    
    <th>
      Behavior and action
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Reactive props, refs, or getters
    </td>
    
    <td>
      Updates patch the existing head entry.
    </td>
  </tr>
  
  <tr>
    <td>
      Unmount through <code>
        v-if
      </code>
      
       or app teardown
    </td>
    
    <td>
      With head cleanup #1020, stopping the owner scope disposes the entry.
    </td>
  </tr>
  
  <tr>
    <td>
      Vapor child cached by <code>
        <KeepAlive>
      </code>
    </td>
    
    <td>
      Automatic head deactivation is unsupported. Its title and tags can remain while inactive.
    </td>
  </tr>
  
  <tr>
    <td>
      Calls after an asynchronous callback loses setup context
    </td>
    
    <td>
      Create the entry before awaiting. Update reactive values or patch that entry afterward.
    </td>
  </tr>
  
  <tr>
    <td>
      Calls without an active effect scope
    </td>
    
    <td>
      Provide the head explicitly and dispose entries yourself. Injection alone does not establish cleanup ownership.
    </td>
  </tr>
</tbody>
</table>

For cached Vapor pages, keep page metadata in a VDOM component that owns the activation lifecycle.
Alternatively, use `v-if` without `<KeepAlive>` when unmounting is acceptable.
Changing a component to `v-show` preserves its scope and head entries.

For asynchronous work, follow [Create the Entry Before Awaiting](https://unhead.unjs.io/docs/vue/head/guides/core-concepts/reactivity-and-context#create-the-entry-before-awaiting).
Passing an explicit head after an `await` restores access to Unhead, but does not restore the original effect scope.

## Script consumers

With script cleanup #1021, `useScript()` captures the effect scope that owns the consumer.
On scope disposal, it releases that consumer's load and error callbacks.
Callbacks registered later through a retained handle still belong to the captured scope.
Registrations through a stopped owner's handle are inert.

With `{ scope: true }`, disposal also aborts the consumer's signal and cancels its pending triggers.
Other consumers can continue using the shared script.
Unmount cleanup keeps the shared script tag available for reuse; it does not undo JavaScript that already executed.

The default loading trigger differs between rendering modes.
The Vue adapter defers the default trigger to `onMounted` when it has a VDOM instance.
Vapor falls through to the core client trigger.
If you require loading after mount, pass an explicit callback trigger:

```vue [src/Analytics.vue]
<script setup lang="ts" vapor>
import { useScript } from '@unhead/vue'
import { onMounted } from 'vue'

useScript('https://example.com/analytics.js', {
  trigger: load => onMounted(load),
  scope: true,
})
</script>

<template>
  <span>Analytics example</span>
</template>
```

Replace the example URL with a trusted script URL.
See the [`useScript()` reference](https://unhead.unjs.io/docs/head/api/composables/use-script) for manual loading and shared script ownership.

## Coverage and boundaries

The [Vapor fixture](https://github.com/unjs/unhead/blob/334d21712fd23de1602de640a4f0d4cff434a034/packages/vue/test/vapor.e2e.test.ts) compiles a real Vapor SFC and bundles Vue's Vapor browser runtime.
It runs in JSDOM with a VDOM parent and `vaporInteropPlugin`.
It checks reactive titles, repeated unmount and remount, script callbacks, and shared consumers.

This fixture does not verify pure `createVaporApp()` apps, browser execution, SSR, or hydration.
KeepAlive deactivation is a known separate limitation.
Use composables for this integration. The fixture does not establish Vapor compatibility for Unhead's render-function components or Options API integration.

## Sitemap

See the full [sitemap](https://unhead.unjs.io/sitemap.md) for all pages.
