Last Updated
Published

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 Vue is straightforward, certain scenarios can be tricky. Let's start with the essential patterns you should follow.

Vue Title Best Practices

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

1. Use a title template

useHead({
  title: 'Home',
  titleTemplate: '%s %separator %siteName',
  templateParams: {
    separator: '·', // choose a seperator, i like using this one
    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 (Nuxt only)

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

SEO Utils v5.0.2
792
81
SEO utilities to improve your Nuxt sites discoverability and shareability.

Understanding the Title Tag in Vue

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

In Vue, 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 Vue'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:

<script setup lang="ts">
useHead({
  title: 'Home'
})
</script>

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.

<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>

Setting Up Title Templates in Vue

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.

<script setup lang="ts">
useHead({
  title: 'Home',
  titleTemplate: '%s %seperator MySite'
})
</script>

Template params like %s act as placeholders that get replaced with your page title and separator.

Template Params

You may ask why we don't just use a function for the title template, and while this is supported, it can create issues with SSR and hydration.

Instead, it's recommended to use the params. Out-of-the-box, Unhead provides:

TokenDescription
%sThe current page title.
%seperatorThe separator, defaults to a pipe character |.

The %separator token is smart - it only appears between content and automatically removes itself when the title is empty or when multiple separators would appear.

Define custom template params to maintain consistent formatting:

<script setup lang="ts">
useHead({
  title: 'Home',
  titleTemplate: '%s %seperator %siteName',
  templateParams: {
    seperator: '',
    siteName: 'MySite'
  }
})
</script>

As we'll likely only ever have one root title template, you can set this globally in your nuxt.config.ts file.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s %separator %siteName',
    }
  }
})

I'd suggest choosing your own separator as the '|' is a bit ugly in my opinion, you can try:

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

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!' }
  ]
})

Resetting the Title Template

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

<script lang="ts" setup>
useHead({
  title: 'Home',
  titleTemplate: null
})
</script>

Social Share Titles in Vue

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.

<script setup lang="ts">
useSeoMeta({
  titleTemplate: '%s %seperator 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 %seperator 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 %seperator 10 reasons to eat more broccoli.',
})
</script>

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 v5.0.2
792
81
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 Enahcned 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.

<script lang="ts" setup>
useSeoMeta({
  titleTemplate: '%s %seperator Health Tips',
  title: 'Home',
})
</script>