SEO и мета-теги

[object Object]

Управление тегами в head в Nuxt реализовано на Unhead: разумные значения по умолчанию, композаблы и настройки для мета-тегов и SEO.

Конфиг Nuxt

Свойство app.head в nuxt.config.ts задаёт статичные теги head для всего приложения.

Реактивные данные здесь задать нельзя. Для реактивности используйте useHead() в app.vue.

Здесь удобно задавать неизменяемые теги: заголовок по умолчанию, язык, favicon.

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      title: 'Nuxt', // заголовок по умолчанию
      htmlAttrs: {
        lang: 'en',
      },
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      ],
    },
  },
})

Также можно задать любые ключи из раздела Types.

Теги по умолчанию

Nuxt подставляет часть тегов сам, чтобы сайт работал из коробки:

  • viewport: width=device-width, initial-scale=1
  • charset: utf-8

Переопределить их можно через соответствующие ключи:

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      charset: 'utf-16',
      viewport: 'width=device-width, initial-scale=1, maximum-scale=1',
    },
  },
})

useHead

Композабл useHead принимает реактивные данные и позволяет управлять тегами head из кода.

app/app.vue
<script setup lang="ts">
useHead({
  title: 'Моё приложение',
  meta: [
    { name: 'description', content: 'Мой замечательный сайт.' },
  ],
  bodyAttrs: {
    class: 'test',
  },
  script: [{ innerHTML: 'console.log(\'Привет, мир\')' }],
})
</script>

Рекомендуется ознакомиться с композаблами useHead и useHeadSafe.

useSeoMeta

Композабл useSeoMeta задаёт SEO мета-теги объектом с полной типизацией.

Так проще избежать опечаток и типичных ошибок (например, name вместо property).

app/app.vue
<script setup lang="ts">
useSeoMeta({
  title: 'Мой замечательный сайт',
  ogTitle: 'Мой замечательный сайт',
  description: 'Это мой замечательный сайт — расскажу о нём.',
  ogDescription: 'Это мой замечательный сайт — расскажу о нём.',
  ogImage: 'https://example.com/image.png',
  twitterCard: 'summary_large_image',
})
</script>
Узнать больше Docs > 4 X > API > Composables > Use Seo Meta.

Компоненты

Помимо useHead, теги head можно задавать в шаблоне компонентами: <Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html>, <Head>. Регистр важен — так мы не конфликтуем с нативными HTML-тегами.

<Head> и <Body> могут содержать вложенные мета-теги (для удобства), но порядок вложенности не влияет на то, где они окажутся в итоговом HTML.

app/app.vue
<script setup lang="ts">
const title = ref('Привет, мир')
</script>

<template>
  <div>
    <Head>
      <Title>{{ title }}</Title>
      <Meta
        name="description"
        :content="title"
      />
      <Style>
        body { background-color: green; }
      </Style>
    </Head>

    <h1>{{ title }}</h1>
  </div>
</template>

Рекомендуется оборачивать теги в <Head> или <Html> — дедупликация будет предсказуемее.

Если нужно дублировать теги на границе клиент–сервер, задайте атрибут key у компонента <Head>.

Типы

Ниже — нереактивные типы для useHead, app.head и компонентов.

interface MetaObject {
  title?: string
  titleTemplate?: string | ((title?: string) => string)
  templateParams?: Record<string, string | Record<string, string>>
  base?: Base
  link?: Link[]
  meta?: Meta[]
  style?: Style[]
  script?: Script[]
  noscript?: Noscript[]
  htmlAttrs?: HtmlAttributes
  bodyAttrs?: BodyAttributes
}

Подробные типы: @unhead/vue.

Возможности

Реактивность

Поддерживаются вычисляемые значения, геттеры и реактивные объекты.

<script setup lang="ts">
const description = ref('Мой замечательный сайт.')

useHead({
  meta: [
    { name: 'description', content: description },
  ],
})
</script>

Шаблон заголовка (titleTemplate)

Опция titleTemplate задаёт шаблон заголовка (например, добавление имени сайта на каждой странице).

titleTemplate может быть строкой (плейсхолдер %s подставит заголовок) или функцией.

Функцию нельзя задать в nuxt.config; задавайте её в app.vue, чтобы она применялась ко всем страницам:

<script setup lang="ts">
useHead({
  titleTemplate: (titleChunk) => {
    return titleChunk ? `${titleChunk} - Site Title` : 'Site Title'
  },
})
</script>

Если на другой странице задать заголовок My Page через useHead, вкладка покажет «My Page - Site Title». Передача null даст только «Site Title».

Параметры шаблона (templateParams)

templateParams добавляет в titleTemplate плейсхолдеры помимо %s для более гибкого формирования заголовка.

<script setup lang="ts">
useHead({
  titleTemplate: (titleChunk) => {
    return titleChunk ? `${titleChunk} %separator %siteName` : '%siteName'
  },
  templateParams: {
    siteName: 'Site Title',
    separator: '-',
  },
})
</script>

Теги в body

Для подходящих тегов можно указать tagPosition: 'bodyClose', чтобы вставить их в конец <body>.

Пример:

<script setup lang="ts">
useHead({
  script: [
    {
      src: 'https://third-party-script.com',
      // допустимые значения: 'head' | 'bodyClose' | 'bodyOpen'
      tagPosition: 'bodyClose',
    },
  ],
})
</script>

Примеры

С definePageMeta

В директории app/pages/ можно комбинировать definePageMeta и useHead для метаданных в зависимости от маршрута.

Заголовок страницы задаётся так (извлекается при сборке макросом, динамически задать нельзя):

pages/some-page.vue
<script setup lang="ts">
definePageMeta({
  title: 'Some Page',
})
</script>

В макете можно использовать заданные метаданные маршрута:

layouts/default.vue
<script setup lang="ts">
const route = useRoute()

useHead({
  meta: [{ property: 'og:title', content: `App Name - ${route.meta.title}` }],
})
</script>
Прочитайте и отредактируйте живой пример в Docs > 4 X > Examples > Features > Meta Tags.
Узнать больше Docs > 4 X > Directory Structure > App > Pages > #page Metadata.

Динамический заголовок

В примере ниже titleTemplate задаётся строкой с плейсхолдером %s или функцией для гибкой подстановки заголовка по маршрутам:

app/app.vue
<script setup lang="ts">
useHead({
  titleTemplate: '%s - Site Title',
})
</script>
app/app.vue
<script setup lang="ts">
useHead({
  titleTemplate: (productCategory) => {
    return productCategory
      ? `${productCategory} - Site Title`
      : 'Site Title'
  },
})
</script>

В nuxt.config заголовок тоже можно задать, но не динамически. Для динамики рекомендуется titleTemplate в app.vue — он применится ко всем маршрутам.

Внешний CSS

Подключение Google Fonts через свойство link композабла useHead или компонент <Link>:

<script setup lang="ts">
useHead({
  link: [
    {
      rel: 'preconnect',
      href: 'https://fonts.googleapis.com',
    },
    {
      rel: 'stylesheet',
      href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
      crossorigin: '',
    },
  ],
})
</script>