Page Titles in Nuxt

Learn how to master page titles in Nuxt using useHead, title templates, and SEO best practices. Includes reactive titles, social sharing, and template params.

Introduction

Page titles are crucial for SEO. They're your primary call-to-action in search results and help users understand your page's content and context.

<head>
  <title>Mastering Titles in Nuxt · Nuxt SEO<</title>
</head>

While setting page titles in Nuxt is straightforward, certain scenarios can be tricky. Let's start with the essential patterns you should follow.

Nuxt Title Best Practices

Here are the key practices for handling page titles in Nuxt:

1. Use a title template

export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s — %siteName',
      templateParams: {
        siteName: 'MyApp' // set a site name
      }
    }
  }
})

2. Always use reactive data with useHead()

Makes sure our page titles are always reactive, I'd personally recommend using computed getter syntax which is the VueUse way.

const title = ref('Loading...')

useHead({
  // computed getter syntax
  title: () => `${title.value} - hold up!`
})

3. Use useSeoMeta() for SEO meta tags and titles.

useSeoMeta({
  title: 'Home',
  description: 'Welcome to MyApp',
})

4. Leverage template params for consistency

One of my favourite ways of using template params is to make sure we always have an og:title set. This can be done globally with one line of code.

You can use template params in any meta tag, not just the title.

// will always use the page title
useSeoMeta({ ogTitle: '%s' })
  1. Use Nuxt SEO Utils for advanced features

If you're not using Nuxt SEO Utils module, then you're adding a lot of extra work for yourself.

SEO Utils v7.0.19
1.4M
117
SEO utilities to improve your Nuxt sites discoverability and shareability.

Understanding the Title Tag in Nuxt

The <title> tag displays text in browser tabs and typically appears as your page's heading in search engine results (SERPs).

In Nuxt, you might be tempted to set titles directly:

// ❌ Careful! This won't work in SSR
document.title = 'Home'

This approach has two major issues:

It breaks during Server-Side Rendering (SSR) Search engines may not properly index your titles

Instead, use Nuxt's head manager Unhead (already included in Nuxt). New to SEO titles? Check out Google's guide on Influencing your title links in search results.

Dynamic Page Titles with useHead()

Now that we understand why direct title manipulation won't work, let's use Unhead's useHead() composable to set titles properly:

input.vue
<script setup lang="ts">
useHead({
  title: 'Home'
})
</script>
output.html
<head>
  <title>Home</title>
</head>

This single line creates an SSR-friendly title that search engines can read. The composable handles all the complexity of managing your document head in both client and server environments.

You can use this in any component and set any <head> tag you like.

<script setup lang="ts">
useHead({
  title: 'Home',
  meta: [
    { name: 'description', content: 'Welcome to MyApp' }
  ]
})

Reactivity with useHead()

As we're using Vue, we may be dealing with dynamic data that contains the page title, meaning we can only set the title once the data has been fetched.

Unhead has first-party Vue support, supporting any input as a ref, reactive or computed value.

A red flag you may come across in your code is if you find yourself destructing reactive data before passing it to useHead(){lang="ts}.

useHead({
  title: myTitle.value // ❌ Avoid destructuring reactive data
})

Instead, pass the reactive data directly to useHead(){lang="ts} to ensure it's reactive.

useHead({
  title: myTitle // ✅ Pass in reactive data, don't destruct it
})

This is important when we may have data that gets refreshed or updated, such as a blog post title or user profile name.

<script setup lang="ts">
const { data } = await useAsyncData(() => fetchPostData())
const title = computed(() => data.value?.title)
useHead({
  title,
})
</script>

SEO Concerns

Always set page titles during server-side rendering (SSR) to ensure search engines can read them. While SPA pages may have their titles honored by search engines, it's not guaranteed.

input.vue
<script setup lang="ts">
const postTitle = ref('Loading...')
useHead({
  title: postTitle
})
// ❌ Avoid fetching data onMounted, it won't be available in SSR
onMounted(async () => {
  const data = await fetchPostData()
  postTitle.value = data.title
})
</script>
output.html
<head>
  <title>Loading...</title>
</head>

Setting Up Title Templates in Nuxt

You may notice that most people set up their titles with a site name and a separator, this is seen as a best practice as it can help with brand recognition and SEO.

<head>
  <title>Home | MySite</title>
</head>

Creating your own title like this is simple using useHead() with a Title Template.

input.vue
<script setup lang="ts">
useHead({
  title: 'Home',
  titleTemplate: '%s | MySite'
})
</script>
output.html
<head>
  <title>Home | MySite</title>
</head>

The %s token is a special placeholder that get replaced with your page title or an empty string if no title is set.

Resetting the Title Template

If you need to reset the title template for a specific page, you can pass null to the titleTemplate option.

input.vue
<script lang="ts" setup>
useHead({
  title: 'Home',
  titleTemplate: null
})
</script>
output.html
<head>
  <title>Home</title>
</head>

Template Params

Template params provide a powerful way to create dynamic titles and meta tags. While Unhead v2 includes the %s token by default for the current page title, additional template params require installing the Template Params plugin:

import { TemplateParamsPlugin } from '@unhead/vue'

// In your app setup
const head = injectHead()
head.use(TemplateParamsPlugin)

Once the plugin is installed, you can use custom template params throughout your application as well as the %separator param.

Define custom template params to maintain consistent formatting:

input.vue
<script setup lang="ts">
useHead({
  title: 'Home',
  titleTemplate: '%s %separator %siteName',
  templateParams: {
    separator: 'x',
    siteName: 'MySite'
  }
})
</script>
output.html
<head>
  <title>Home x MySite</title>
</head>

You can choose your own separator character when setting up your title template:

type separator = '|' | '-' | '' | '' | '·' | '❤️'

You can use template params in other head tags too, such as meta descriptions and open graph tags.

useHead({
  templateParams: {
    siteName: 'MyApp'
  },
  title: 'Home',
  meta: [
    { name: 'description', content: 'Welcome to %siteName - where we make awesome happen' },
    { property: 'og:title', content: 'Home | %siteName' },
    { property: 'og:description', content: 'Check out %siteName today!' }
  ]
})

Social Share Titles in Nuxt

Social platforms use different meta tags for sharing titles.

Nuxt X Share
Nuxt X Share

In the above we can see the title "Nuxt: The Intuitive Vue Framework".

This title is set using the twitter:title meta tag and will fall back to the og:title meta tag if not set.

Remembering how to use the meta tags can be annoying, so we can use the useSeoMeta() composable to set these up.

input.vue
<script setup lang="ts">
useSeoMeta({
  titleTemplate: '%s %separator Health Tips',
  title: 'Why you should eat more broccoli',
  // og title is not effected by titleTemplate, we can use template params here if we need
  ogTitle: 'Hey! Health Tips %separator 10 reasons to eat more broccoli.',
  // explicit twitter title is only needed when we want to display something just for X
  twitterTitle: 'Hey X! Health Tips %separator 10 reasons to eat more broccoli.',
})
</script>
output.html
<head>
  <title>Why you should eat more broccoli | Health Tips</title>
  <meta property="og:title" content="Hey! Health Tips — 10 reasons to eat more broccoli." />
  <meta name="twitter:title" content="Hey X! Health Tips — 10 reasons to eat more broccoli." />
</head>

Nuxt SEO Utils

Nuxt SEO Utils is a powerful module that helps you manage your technical SEO. If you're using the Nuxt SEO module it's already included for you.

SEO Utils v7.0.19
1.4M
117
SEO utilities to improve your Nuxt sites discoverability and shareability.

It has a number of features that can help you manage your titles, such as:

Fallback Titles

Usually we map our routes in a way that the page title and be inferred. For example, consider we have a route called /about-us, it seems reasonable that we'd have a title of "About Us".

With Nuxt SEO Utils, this will in fact be the default behavior. If you don't set a title, it will use the last slug segment as the title.

You can read more about it in the Enhanced Title guide.

Automatic Social Share Titles

Most of the time our <title> and our <meta property="og:title"> will be the same.

It's easy to forget to set the og:title meta tag, so Nuxt SEO Utils will automatically set this for you based on the page title, it will even ignore the title template.

input.vue
<script lang="ts" setup>
useSeoMeta({
  titleTemplate: '%s %separator Health Tips',
  title: 'Home',
})
</script>
output.html
<head>
  <title>Home | Health Tips</title>
  <meta property="og:title" content="Home" />
</head>