In this article, I will explain how to dynamically update elements inside the <head> tag, such as <title> and <meta> tags, on a per-page basis in Vue 3.
Motivation: Dynamically Updating the Head Tag in an SPA
When building a Single Page Application (SPA) with Vue, Vue only manages the virtual DOM inside the <body> element. As a result, the <head> section lies outside Vue’s scope and cannot be customized directly.
This means that every page ends up sharing the same contents in the <head> tag, making elements that should vary by page—such as the page title and description—remain unchanged. This can negatively impact usability. From an SEO perspective, it is also desirable to update the contents of meta tags dynamically.
Plugins Available for Vue 2 Do Not Work with Vue 3
When searching online for ways to modify the <title> and <meta> tags in Vue.js, many articles recommend a plugin called vue-head.
However, this plugin appears to support only Vue 2.x (I could not find any information about Vue 3 support on GitHub). While looking for an alternative, I came across unhead, a plugin specifically designed for head management.
Installing @unhead/vue
Let’s install @unhead/vue.
If you plan to use Unhead with Vue 3, I recommend reading the official setup guide first.
For npm users:
npm install @unhead/vue
For Yarn users:
yarn add @unhead/vue
Preparing to Use @unhead/vue
First, register the plugin with your Vue application:
import { createApp } from 'vue'
import { createHead } from '@unhead/vue'
const app = createApp()
const head = createHead()
app.use(head)
app.mount('#app')
If you are using the Composition API style, the setup is now complete.
If you are using the Options API style, you will also need to register a mixin as described in the next section.
Using Unhead with the Vue 3 Options API
Unhead is primarily designed for Vue 3 applications written with the Composition API. The built-in Options API support is only available for Vue 2.
Therefore, if you want to use Unhead with the Vue 3 Options API, you need to register the following mixin:
import { createApp } from 'vue'
import { VueHeadMixin, createHead } from '@unhead/vue'
const app = createApp()
const head = createHead()
app.mixin(VueHeadMixin)
// ...
Usage
Composition API
With the Composition API, you can use the useHead() function.
Pass an object describing the desired head contents as the first argument. The second argument can be used to apply options to all tags defined in the first argument.
The following example demonstrates how to update the head tag:
<script setup lang="ts">
import { useHead } from '@unhead/vue'
useHead({
title: 'KURO DIGITAL LOG',
meta: [
{
name: 'description',
content: 'A blog about web development and programming.',
},
],
script: [
{
src: '/hoge/hoge.js',
tagPosition: 'bodyClose',
},
],
})
</script>
In this example, only the first argument is passed to useHead().
Within the script configuration, the tagPosition option is set to bodyClose. This instructs Unhead to place the <script> tag at the end of the <body> element instead of inside the <head>.
As you can see, Unhead provides options that allow behavior beyond simply modifying the contents of the head tag. For more details, the official documentation is very helpful.
Options API
With the Options API, head settings are defined through a head() method.
Don’t forget to register the mixin mentioned earlier.
Here is a sample implementation:
<script>
export default {
head() {
return {
title: 'KURO DIGITAL LOG',
meta: [
{
name: 'description',
content: 'A blog about web development and programming.',
},
],
script: [
{
src: '/hoge/hoge.js',
tagPosition: 'bodyClose',
},
],
}
},
// ...
}
</script>
<template>
...
</template>
Conclusion
Although there is plenty of information available on modifying the contents of the <head> tag in Vue 2, I found relatively little information for Vue 3, which ended up costing me more time than expected.
I hope this article helps anyone facing the same issue.