Метаданные

Next.js предоставляет API Metadata, который можно использовать для определения метаданных вашего приложения (например, тегов meta и link внутри HTML-элемента head) для улучшения SEO и возможности совместного использования в интернете.

Существует два способа добавления метаданных в ваше приложение:

  • Конфигурационные метаданные: Экспортируйте статический объект metadata или динамическую функцию generateMetadata в файле layout.js или page.js.
  • Файловые метаданные: Добавьте статические или динамически генерируемые специальные файлы в сегменты маршрутов.

С этими опциями Next.js автоматически сгенерирует соответствующие элементы <head> для ваших страниц. Вы также можете создавать динамические OG-изображения с помощью конструктора ImageResponse.

Статические метаданные

Для определения статических метаданных экспортируйте объект Metadata из файла layout.js или статического page.js.

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}
export const metadata = {
  title: '...',
  description: '...',
}

export default function Page() {}

Для всех доступных опций см. Справочник API.

Динамические метаданные

Вы можете использовать функцию generateMetadata для получения метаданных, требующих динамических значений.

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // чтение параметров маршрута
  const id = params.id

  // получение данных
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // опциональное использование и расширение (а не замена) родительских метаданных
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
  // чтение параметров маршрута
  const id = params.id

  // получение данных
  const product = await fetch(`https://.../${id}`).then((res) => res.json())

  // опциональное использование и расширение (а не замена) родительских метаданных
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}

export default function Page({ params, searchParams }) {}

Для всех доступных параметров см. Справочник API.

Полезно знать:

  • И статические, и динамические метаданные через generateMetadata поддерживаются только в серверных компонентах.
  • Запросы fetch автоматически мемоизируются для одинаковых данных в generateMetadata, generateStaticParams, макетах, страницах и серверных компонентах. React cache можно использовать, если fetch недоступен.
  • Next.js будет ждать завершения получения данных внутри generateMetadata перед потоковой передачей UI клиенту. Это гарантирует, что первая часть потокового ответа включает теги <head>.

Файловые метаданные

Для метаданных доступны следующие специальные файлы:

Вы можете использовать их для статических метаданных или программно генерировать эти файлы с помощью кода.

Для реализации и примеров см. Справочник API по файлам метаданных и Динамическая генерация изображений.

Поведение

Файловые метаданные имеют более высокий приоритет и переопределяют любые конфигурационные метаданные.

Поля по умолчанию

Есть два тега meta по умолчанию, которые всегда добавляются, даже если маршрут не определяет метаданные:

  • Тег meta charset устанавливает кодировку символов для веб-сайта.
  • Тег meta viewport устанавливает ширину и масштаб области просмотра для адаптации к разным устройствам.
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

Полезно знать: Вы можете переопределить тег viewport по умолчанию.

Порядок оценки

Метаданные оцениваются в порядке от корневого сегмента до сегмента, ближайшего к конечному page.js. Например:

  1. app/layout.tsx (Корневой макет)
  2. app/blog/layout.tsx (Вложенный макет блога)
  3. app/blog/[slug]/page.tsx (Страница блога)

Объединение

Следуя порядку оценки, объекты метаданных, экспортированные из нескольких сегментов одного маршрута, поверхностно объединяются для формирования окончательного вывода метаданных маршрута. Дублирующиеся ключи заменяются в соответствии с их порядком.

Это означает, что метаданные с вложенными полями, такими как openGraph и robots, определенные в более раннем сегменте, переопределяются последним сегментом, который их определяет.

Переопределение полей

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// Вывод:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

В примере выше:

  • title из app/layout.js заменяется на title из app/blog/page.js.
  • Все поля openGraph из app/layout.js заменяются в app/blog/page.js, потому что app/blog/page.js устанавливает метаданные openGraph. Обратите внимание на отсутствие openGraph.description.

Если вы хотите поделиться некоторыми вложенными полями между сегментами, переопределяя другие, вы можете вынести их в отдельную переменную:

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

В примере выше OG-изображение используется совместно между app/layout.js и app/about/page.js, в то время как заголовки различаются.

Наследование полей

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/about/page.js
export const metadata = {
  title: 'About',
}

// Вывод:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

Примечания

  • title из app/layout.js заменяется на title из app/about/page.js.
  • Все поля openGraph из app/layout.js наследуются в app/about/page.js, потому что app/about/page.js не устанавливает метаданные openGraph.

Динамическая генерация изображений

Конструктор ImageResponse позволяет генерировать динамические изображения с использованием JSX и CSS. Это полезно для создания изображений для социальных сетей, таких как Open Graph изображения, Twitter Cards и других.

ImageResponse использует Edge Runtime, и Next.js автоматически добавляет правильные заголовки для кэшированных изображений на границе, что помогает улучшить производительность и уменьшить повторные вычисления.

Для использования импортируйте ImageResponse из next/server:

app/about/route.js
import { ImageResponse } from 'next/server'

export const runtime = 'edge'

export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}

ImageResponse хорошо интегрируется с другими API Next.js, включая Обработчики маршрутов и файловые метаданные. Например, вы можете использовать ImageResponse в файле opengraph-image.tsx для генерации Open Graph изображений во время сборки или динамически во время запроса.

ImageResponse поддерживает общие свойства CSS, включая flexbox и абсолютное позиционирование, пользовательские шрифты, перенос текста, выравнивание по центру и вложенные изображения. См. полный список поддерживаемых свойств CSS.

Полезно знать:

  • Примеры доступны в Vercel OG Playground.
  • ImageResponse использует @vercel/og, Satori и Resvg для преобразования HTML и CSS в PNG.
  • Поддерживается только Edge Runtime. Стандартная Node.js среда выполнения не работает.
  • Поддерживаются только flexbox и подмножество свойств CSS. Сложные макеты (например, display: grid) не работают.
  • Максимальный размер бандла — 500KB. Размер бандла включает ваш JSX, CSS, шрифты, изображения и другие ресурсы. Если вы превысите лимит, рассмотрите возможность уменьшения размера ресурсов или их получения во время выполнения.
  • Поддерживаются только форматы шрифтов ttf, otf и woff. Для максимальной скорости разбора шрифтов предпочтительнее ttf или otf, а не woff.

JSON-LD

JSON-LD — это формат структурированных данных, который может использоваться поисковыми системами для понимания вашего контента. Например, вы можете использовать его для описания человека, события, организации, фильма, книги, рецепта и многих других типов сущностей.

Наша текущая рекомендация для JSON-LD — рендерить структурированные данные как тег <script> в ваших компонентах layout.js или page.js. Например:

export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* Добавьте JSON-LD на вашу страницу */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}
export default async function Page({ params }) {
  const product = await getProduct(params.id)

  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }

  return (
    <section>
      {/* Добавьте JSON-LD на вашу страницу */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

Вы можете проверить и протестировать ваши структурированные данные с помощью Rich Results Test от Google или универсального Schema Markup Validator.

Вы можете типизировать JSON-LD с помощью TypeScript, используя сообществом разработанные пакеты, такие как schema-dts:

import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}