SEO и Meta

Улучшайте SEO приложения на Nuxt через head в конфиге, композаблы и компоненты.

Управление тегами в <head> в Nuxt построено на Unhead. Он задаёт разумные значения по умолчанию, даёт несколько мощных композаблов и множество опций для настройки <head> и SEO-метатегов приложения.

Конфигурация Nuxt

В nuxt.config.ts через app.head можно статически задать <head> для всего приложения.

Здесь нельзя использовать реактивные данные. Для этого лучше useHead() в app.vue.

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

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      title: 'Nuxt', // default fallback title
      htmlAttrs: {
        lang: 'en',
      },
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      ],
    },
  },
})

Ниже в разделе «Типы» перечислены и другие допустимые поля.

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

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

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

Большинству сайтов не нужно менять эти значения по умолчанию; при необходимости их можно обновить через сокращённые поля charset и viewport.

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

useHead

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

app.vue
<script setup lang="ts">
useHead({
  title: 'My App',
  meta: [
    { name: 'description', content: 'My amazing site.' },
  ],
  bodyAttrs: {
    class: 'test',
  },
  script: [{ innerHTML: 'console.log(\'Hello world\')' }],
})
</script>

Мы рекомендуем композаблы useHead и useHeadSafe.

useSeoMeta

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

Так проще не ошибиться в атрибутах — например, не перепутать name и property.

app.vue
<script setup lang="ts">
useSeoMeta({
  title: 'My Amazing Site',
  ogTitle: 'My Amazing Site',
  description: 'This is my amazing site, let me tell you all about it.',
  ogDescription: 'This is my amazing site, let me tell you all about it.',
  ogImage: 'https://example.com/image.png',
  twitterCard: 'summary_large_image',
})
</script>
Узнать больше Docs > 3 X > API > Composables > Use Seo Meta.

Компоненты

Во всех сценариях предпочтительнее useHead, но при желании метатеги можно описать в шаблоне компонентами.

Nuxt предоставляет <Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html> и <Head> для работы с <head> из разметки. Имена этих компонентов должны начинаться с заглавной буквы — так они не пересекаются с обычными HTML-тегами.

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

app.vue
<script setup lang="ts">
const title = ref('Hello World')
</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.

Возможности

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

Реактивность поддерживается для всех свойств путем предоставления вычисляемого значения (computed), геттера или реактивного объекта.

<script setup lang="ts">
const description = ref('My amazing site.')

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

Шаблон названия

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

Это может быть строка с %s вместо заголовка страницы или функция.

Функцию нельзя задать в nuxt.config; для полного контроля указывайте titleTemplate в app.vue — тогда шаблон действует на все страницы:

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

Если на другой странице через useHead задать заголовок Моя страница, на вкладке будет «Моя страница - Заголовок сайта». Передав null, получите только «Заголовок сайта».

Параметры шаблона (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',
      // valid options are: 'head' | 'bodyClose' | 'bodyOpen'
      tagPosition: 'bodyClose',
    },
  ],
})
</script>

Примеры

Используя definePageMeta

В каталоге 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 > 3 X > Examples > Features > Meta Tags.
Узнать больше Docs > 3 X > Directory Structure > Pages > #page Metadata.

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

В примере ниже titleTemplate задается либо как строка с заполнителем %s, либо как функция, что позволяет более гибко устанавливать заголовок страницы динамически для каждого маршрута вашего приложения Nuxt:

app.vue
<script setup lang="ts">
useHead({
  // as a string,
  // where `%s` is replaced with the title
  titleTemplate: '%s - Site Title',
})
</script>
app.vue
<script setup lang="ts">
useHead({
  // or as a function
  titleTemplate: (productCategory) => {
    return productCategory
      ? `${productCategory} - Site Title`
      : 'Site Title'
  },
})
</script>

nuxt.config также используется в качестве альтернативного способа установки заголовка страницы. Однако nuxt.config не позволяет сделать заголовок страницы динамическим. Поэтому рекомендуется использовать titleTemplate в файле app.vue для добавления динамического заголовка, который затем применяется ко всем маршрутам вашего приложения Nuxt.

Внешний 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>