Index of every page on this site: llms.txt

How to Set Page Titles in Nuxt

Set dynamic page titles in Nuxt with useHead. Learn title templates, reactive titles, and SSR patterns that include titles in the initial HTML.
Harlan Wilton8 mins read Published Updated
What you'll learn
  • Use useHead() or useSeoMeta() for titles; document.title breaks SSR
  • Title templates append your site name with the %s | MySite pattern
  • Write concise, descriptive titles; Google truncates title links to fit the available display width

Page titles appear in browser tabs and can supply the clickable headline in search results. Google can use other page content for title links, so write a title that matches the page.

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

Page titles work by default 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 accesses a browser-only API and throws during server rendering. 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:

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

const myTitle = ref('Home')

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 leave the loading state in the initial HTML:

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

// onMounted runs after SSR. The initial HTML contains "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:

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

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 ignores titleTemplate, set it explicitly
  ogTitle: 'Health Tips: 10 reasons to eat more broccoli',
  // Set an explicit X 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>

Set twitterTitle explicitly when you want a separate X title.

Title Length

Google does not set a fixed title length. It truncates title links to fit the device width. Put the page's main subject first.

A long title can still describe the page, but users may see only part of it. Avoid unnecessary boilerplate.

Nuxt SEO Module

The Nuxt SEO module handles title defaults, social sharing, and more:

Nuxt SEO 
   
The all-in-one module that brings it all together.

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

Title inference depends on the module configuration. A title template can appear in an inferred og:title. Set ogTitle explicitly when the social title must differ from the composed page title:

useSeoMeta({
  title: 'Home',
  ogTitle: 'Home',
})

After deploying a title edit, check the live HTML and Google's last crawl separately. Reported title updates typically took 1 to 2 days at the serving stage, with slow cases lasting weeks or months.

Recrawling and reprocessing add separate waits. Google can choose another title. See Google Search timelines.

Checklist

Checklist
  • Use useHead() or useSeoMeta() instead of document.title
  • Set up a title template with your site name
  • Keep titles concise and descriptive
  • Set og:title for social sharing
  • Fetch data with useFetch() or useAsyncData() so titles render server-side
  • Test titles render correctly in SSR ("View Source")
Was this page helpful?