---
title: "How to Set Page Titles in Nuxt · Nuxt SEO"
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/mastering-meta/titles"
last_updated: "2026-07-16T12:00:00.000Z"
meta:
  author: "Harlan Wilton"
  description: "Set dynamic page titles in Nuxt with useHead. Learn title templates, reactive titles, and SSR patterns that Google indexes correctly."
  "og:description": "Set dynamic page titles in Nuxt with useHead. Learn title templates, reactive titles, and SSR patterns that Google indexes correctly."
  "og:title": "How to Set Page Titles in Nuxt · Nuxt SEO"
---

Nuxt SEO on GitHub

# **How to Set 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 Wilton](https://x.com/harlan-zw)8 mins read Published **Oct 24, 2024** Updated **Jul 16, 2026**

**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
- Keep titles under 60 characters: longer titles get truncated and are far more likely to be rewritten

Page titles appear in browser tabs and as the clickable headline in search results. Google keeps your `**<title>**` tag as-is [**~~around 87% of the time~~**](https://developers.google.com/search/blog/2021/09/more-info-about-titles). Third-party studies that count any modification, including truncation and casing changes, put the figure closer to [**~~76% of titles changed in some way~~**](https://searchengineland.com/google-changed-76-of-title-tags-in-q1-2025-heres-what-that-means-454847), so a well-formed title is your best shot at controlling what searchers see.

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

```ts
// 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:

```ts
// ❌ 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~~**](https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics) for client-side updates.

Nuxt includes [**~~Unhead~~**](https://unhead.unjs.io/) which handles both SSR and client-side updates automatically. For Google's official guidance, see [**~~Influencing your title links~~**](https://developers.google.com/search/docs/appearance/title-link).

## Setting Titles with `**useHead()**`

The [`**useHead()**`](https://unhead.unjs.io/docs/head/api/composables/use-head) composable sets titles that work in SSR and client-side navigation:

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

output.html

```html
<head>
  <title>Home</title>
</head>
```

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

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

```ts
const myTitle = ref('Home')

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

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

Computed getter syntax works for derived titles:

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

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

```vue
<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~~**](https://developers.google.com/search/docs/appearance/title-link#page-titles) adding your site name with a delimiter:

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

Use `**titleTemplate**` with a [**~~title template~~**](https://unhead.unjs.io/docs/head/guides/core-concepts/titles):

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

output.html

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

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

output.html

```html
<head>
  <title>Home</title>
</head>
```

## Template Params

Set template params globally in `**nuxt.config.ts**`:

```ts
export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s %separator %siteName',
      templateParams: {
        separator: '·',
        siteName: 'MySite'
      }
    }
  }
})
```

output.html

```html
<head>
  <title>Home · MySite</title>
</head>
```

Common separators: `**|**` `**-**` `**.**` `**•**` `**·**`

Template params work in meta tags too:

```ts
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()**`](https://unhead.unjs.io/docs/head/api/composables/use-seo-meta) to set these:

![Nuxt X Share](https://nuxtseo.com/nuxt-x-share.png)*Nuxt X Share*

input.vue

```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',
  // twitter:title only needed if different from og:title
  twitterTitle: 'Hey X! 10 reasons to eat more broccoli',
})
</script>
```

output.html

```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 truncates titles at roughly 600px of display width, which works out to 50-60 characters depending on the letters used. The cutoff is pixel-based, so two titles of identical length can truncate differently.

Length also affects rewrites. [**~~Zyppy's study of 80,000+ titles~~**](https://zyppy.com/seo/google-title-rewrite-study/) found the 51-60 character range had the lowest rewrite rates (39-42%), while titles over 70 characters were rewritten every time. A [**~~2025 follow-up study~~**](https://searchengineland.com/google-changed-76-of-title-tags-in-q1-2025-heres-what-that-means-454847) found titles Google left unchanged averaged 44 characters.

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

## 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.](https://nuxtseo.com/docs/nuxt-seo/getting-started/introduction)

Routes like `**/about-us**` automatically get "About Us" as the fallback title if no title is set. Read more in the [**~~Enhanced Title~~**](https://nuxtseo.com/docs/seo-utils/guides/fallback-title) guide.

The module also automatically sets `**og:title**` based on your page title (ignoring the title template):

input.vue

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

output.html

```html
<head>
  <title>Home | Health Tips</title>
  <meta property="og:title" content="Home" />
</head>
```

## Checklist

**Checklist**

- Use `**useHead()**` or `**useSeoMeta()**` instead of `**document.title**`
- Set up a title template with your site name
- Keep titles under 60 characters
- 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")

[**The 2026 SEO Checklist for Nuxt & Vue ** Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.](https://nuxtseo.com/learn-seo/checklist) [Haven't launched yet? Start with the **Pre-Launch Warmup**](https://nuxtseo.com/learn-seo/pre-launch-warmup)

---

### **Related **

[**Meta Descriptions**](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/descriptions)

[**Social Sharing Tags**](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/open-graph)

[**Rendering Modes**](https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/rendering)

[**Nuxt SEO Utils**](https://nuxtseo.com/docs/seo-utils/getting-started/introduction)

### **Open Source **

[**unjs/unhead**** Public **](https://github.com/unjs/unhead)

[**Mastering Meta** Set titles, descriptions, Open Graph, and Schema.org in Nuxt with useSeoMeta, plus the priority order when tags conflict.](https://nuxtseo.com/learn-seo/nuxt/mastering-meta) [**Meta Description** Google rewrites most meta descriptions anyway. Set yours with useSeoMeta in Nuxt, then put the real effort into content that earns the click.](https://nuxtseo.com/learn-seo/nuxt/mastering-meta/descriptions)

**On this page**

- [Quick Reference](#quick-reference)
- [Why Not document.title?](#why-not-documenttitle)
- [Setting Titles with useHead()](#setting-titles-with-usehead)
- [Title Templates](#title-templates)
- [Template Params](#template-params)
- [Social Share Titles](#social-share-titles)
- [Title Length](#title-length)
- [Nuxt SEO Module](#nuxt-seo-module)
- [Checklist](#checklist)