How to Set Page Titles in Nuxt
- Use
useHead()oruseSeoMeta()for titles;document.titlebreaks SSR - Title templates append your site name with the
%s | MySitepattern - 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><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><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:
<script lang="ts" setup>
useHead({
title: 'Home',
titleTemplate: null
})
</script><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'
}
}
}
})<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:

<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><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:
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
- Use
useHead()oruseSeoMeta()instead ofdocument.title - Set up a title template with your site name
- Keep titles concise and descriptive
- Set
og:titlefor social sharing - Fetch data with
useFetch()oruseAsyncData()so titles render server-side - Test titles render correctly in SSR ("View Source")
Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.