Set the page title and meta tags with useHead and useSeoMeta, and build titles from your data.
Why: useSeoMeta is the easiest way to set SEO tags — title, description, and social-share (Open Graph) tags — with full TypeScript hints. Call it in any page’s <script setup>. Note: it’s auto-imported.
<!-- app/pages/index.vue -->
<script setup lang="ts">
useSeoMeta({
title: 'My Site',
description: 'A friendly intro to Nuxt.',
ogTitle: 'My Site',
ogImage: '/og.png',
})
</script>
<template>
<h1>Home</h1>
</template>Why: on a dynamic page the title depends on the data. Fetch first, then pass the value into useSeoMeta — the right title ends up in the page <head>.
<!-- app/pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)
useSeoMeta({
title: () => post.value?.title,
description: () => post.value?.summary,
})
</script>
<template>
<h1>{{ post?.title }}</h1>
</template>Why: for tags useSeoMeta doesn’t cover — extra <link>s, scripts, html attributes — useHead gives you full control of the document head.
<script setup lang="ts">
useHead({
htmlAttrs: { lang: 'en' },
link: [{ rel: 'icon', href: '/favicon.ico' }],
})
</script>Why: to add a site name after every page title (e.g. "Blog · Belinkee"), set a titleTemplate once in app.vue and every page’s title flows through it.
<!-- app/app.vue -->
<script setup lang="ts">
useHead({
titleTemplate: (title) => (title ? `${title} · Belinkee` : 'Belinkee'),
})
</script>
<template>
<NuxtLayout><NuxtPage /></NuxtLayout>
</template>