Page Titles in Nuxt

Set dynamic page titles in Nuxt with useHead. Learn title templates, reactive titles, and SSR patterns that Google indexes correctly.
Harlan WiltonHarlan Wilton8 mins read Published Updated
What you'll learn
  • Use useHead() or useSeoMeta() for titles—document.title breaks SSR
  • Title templates append site name with %s | MySite pattern
  • Keep under 60 characters—Google truncates and rewrites 61-76% of titles

Page titles appear in browser tabs and as the clickable headline in search results. Google uses your <title> tag over 80% of the time when generating title links—though studies show it rewrites 61-76% of titles to some degree.

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

Page titles work out of the box in Nuxt. Use useSeoMeta() or useHead() in any component.

Quick Reference

// Basic title
useHead({ title: 'Home' })

// With template (adds site name)
useHead({
  title: 'Home',
  titleTemplate: '%s | MySite'
})

// Reactive title from data
const post = ref({ title: 'Loading...' })
useHead({
  title: () => post.value.title
})

// SEO-focused (includes og:title)
useSeoMeta({
  title: 'Home',
  ogTitle: 'Home | MySite'
})

Why Not document.title?

You might try setting titles directly:

// ❌ Breaks SSR, may not be indexed
document.title = 'Home'

This fails during server-side rendering—the title won't exist in the initial HTML response. Search engines render JavaScript but may not wait for client-side updates.

Nuxt includes Unhead which handles both SSR and client-side updates automatically. For Google's official guidance, see Influencing your title links.

Setting Titles with useHead()

The useHead() composable sets titles that work in SSR and client-side navigation:

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

Works in any component. You can set other head tags in the same call:

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

Reactive Titles

Unhead accepts refs, reactive objects, and computed values. Don't destructure—pass the reactive reference:

useHead({
  title: myTitle.value // ❌ Loses reactivity
})

useHead({
  title: myTitle // ✅ Stays reactive
})

Computed getter syntax works for derived titles:

const post = ref({ title: 'Loading...' })

useHead({
  title: () => post.value.title // Updates when post changes
})

SSR and SEO

Fetch data during SSR with useFetch() or useAsyncData(). Client-only fetches mean search engines see your loading state:

<script setup lang="ts">
const postTitle = ref('Loading...')
useHead({ title: postTitle })

// ❌ onMounted runs after SSR—Google sees "Loading..."
onMounted(async () => {
  postTitle.value = (await fetchPostData()).title
})
</script>

Use Nuxt's data fetching composables instead:

<script setup lang="ts">
const { data: post } = await useFetch('/api/post')

useHead({
  title: () => post.value?.title || 'Loading...'
})
</script>

Title Templates

Most sites append a site name to titles for brand recognition. Google recommends adding your site name with a delimiter:

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

Use titleTemplate 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 gets replaced with your page title (or empty string if none set).

Disabling the Template

Override the template for specific pages by passing null:

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

Template Params

Set template params globally in nuxt.config.ts:

export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s %separator %siteName',
      templateParams: {
        separator: '·',
        siteName: 'MySite'
      }
    }
  }
})
output.html
<head>
  <title>Home · MySite</title>
</head>

Common separators: | - ·

Template params work in meta tags too:

useHead({
  templateParams: { siteName: 'MyApp' },
  title: 'Home',
  meta: [
    { name: 'description', content: 'Welcome to %siteName' },
    { property: 'og:title', content: 'Home | %siteName' }
  ]
})

Social Share Titles

Social platforms use og:title and twitter:title meta tags. Use useSeoMeta() to set these:

Nuxt X Share
Nuxt X Share
input.vue
<script setup lang="ts">
useSeoMeta({
  title: 'Why you should eat more broccoli',
  titleTemplate: '%s | Health Tips',
  // og:title doesn't use titleTemplate—set it explicitly
  ogTitle: 'Health Tips: 10 reasons to eat more broccoli',
  // twitter:title only needed if different from og:title
  twitterTitle: 'Hey X! 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="Health Tips: 10 reasons to eat more broccoli" />
  <meta name="twitter:title" content="Hey X! 10 reasons to eat more broccoli" />
</head>

Twitter/X falls back to og:title if twitter:title isn't set.

Title Length

Google displays roughly 50-60 characters before truncating. Studies show titles between 51-60 characters have the lowest rewrite rates (39-42%).

Longer titles still get indexed—Google just truncates the display. Front-load important keywords since users may only see the first 50 characters.

Nuxt SEO Utils

Nuxt SEO Utils handles title defaults and social sharing automatically:

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

Fallback Titles

Routes like /about-us automatically get "About Us" as the fallback title if no title is set. Read more in the Enhanced Title guide.

Automatic Social Share Titles

The module automatically sets og:title based on your page title (ignoring 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>