JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Metadata API: статическая генерация метаданных

Metadata API: статическая генерация метаданных

Модуль 4: Node.js, Next.js и Angular
9 уровень , 8 лекция
Открыта

1. Введение

Когда вы открываете сайт, браузер и поисковые системы видят не только саму страницу, но и множество специальных метаданных — это информация, которая не видна пользователю напрямую, но очень важна для:

  • Поисковых систем (SEO): чтобы ваш сайт показывался в Google, Яндексе и т.д.
  • Социальных сетей: чтобы ссылка на страницу выглядела красиво (Open Graph, Twitter Cards).
  • Браузеров: чтобы отображался favicon, правильный заголовок вкладки, цвет темы и т.д.

Пример: если вы напишете в <head> вашего HTML <title>Мой сайт</title>, то это и будет отображаться на вкладке браузера. Но если вы хотите, чтобы при расшаривании ссылки в Telegram появилась красивая карточка с картинкой и описанием — нужны более продвинутые метаданные.

До Next.js 13+

В старых версиях Next.js (и вообще в React-приложениях) для этого использовался компонент <Head> из next/head (или аналогичные пакеты типа react-helmet). Вы вручную писали:


import Head from 'next/head';

export default function Page() {
  return (
    <>
      <Head>
        <title>Моя страница</title>
        <meta name="description" content="Описание страницы" />
        <meta property="og:title" content="Моя страница" />
        <meta property="og:description" content="Описание для соцсетей" />
        <meta property="og:image" content="https://example.com/og-image.png" />
      </Head>
      <main>...</main>
    </>
  );
}

Это работало, но было неудобно, не типобезопасно, не поддерживало Server Components и не позволяло удобно централизовать метаданные для разных уровней вложенности.

Metadata API в Next.js 13+ и 15

Основная идея
В App Router (папка app/) Next.js предлагает новый декларативный способ задания метаданных — через экспорт объекта metadata или функцию generateMetadata в файлах страниц (page.tsx) и layout'ов (layout.tsx). Это позволяет:

  • Генерировать метаданные статически (на этапе сборки), если они не зависят от данных.
  • Централизованно и иерархически описывать метаданные: layout задаёт общие, страница — свои.
  • Гарантировать типобезопасность (TypeScript поддержка!).
  • Автоматически генерировать <title>, <meta>, Open Graph, Twitter Cards и даже favicon.

Базовая схема работы

  • В каждом файле page.tsx или layout.tsx можно экспортировать объект metadata.
  • Next.js собирает метаданные со всех уровней вложенности и "сливает" их в итоговый <head>.
  • Если нужно генерировать метаданные динамически — можно экспортировать функцию generateMetadata, но сегодня мы сосредоточимся на статической генерации.

2. Статическая генерация метаданных: синтаксис и базовые примеры

Экспорт объекта metadata

В самом простом случае вы просто экспортируете объект:


// app/page.tsx (главная страница)
export const metadata = {
  title: 'Главная страница',
  description: 'Добро пожаловать на наш сайт!',
};

Это всё! Next.js автоматически добавит в <head> вашей HTML-страницы:


<title>Главная страница</title>
<meta name="description" content="Добро пожаловать на наш сайт!">

Пример для вложенной страницы


// app/about/page.tsx
export const metadata = {
  title: 'О нас',
  description: 'Узнайте больше о нашей компании!',
};

Если пользователь откроет /about, на вкладке будет "О нас", а в мета-теге — нужное описание.

Что ещё можно указать в metadata?

Объект metadata поддерживает множество полей. Вот основные:

Поле Назначение Пример значения
title Заголовок страницы
'Контакты'
description Описание страницы (SEO, сниппеты)
'Связаться с нами просто!'
keywords Ключевые слова (редко используется)
['react', 'nextjs', 'frontend']
authors Авторы страницы
[{ name: 'Иван Иванов', url: 'https://...' }]
openGraph Open Graph-метаданные для соцсетей
{ title, description, images, ... }
twitter Twitter Cards
{ card, title, description, images }
icons Favicon и другие иконки
{ icon: '/favicon.ico' }
robots Инструкции для поисковых роботов
'index, follow'
themeColor Цвет темы для мобильных браузеров
'#ff6600'

Пример расширенного объекта metadata


// app/page.tsx
export const metadata = {
  title: 'Главная страница',
  description: 'Добро пожаловать на наш сайт!',
  keywords: ['nextjs', 'react', 'метаданные', 'seo'],
  authors: [{ name: 'Иван Иванов', url: 'https://github.com/ivan' }],
  openGraph: {
    title: 'Главная страница',
    description: 'Это наш замечательный сайт на Next.js',
    url: 'https://my-site.ru',
    siteName: 'MySite',
    images: [
      {
        url: 'https://my-site.ru/og-image.png',
        width: 1200,
        height: 630,
        alt: 'Главное изображение сайта',
      },
    ],
    locale: 'ru_RU',
    type: 'website',
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Главная страница',
    description: 'Это наш замечательный сайт на Next.js',
    images: ['https://my-site.ru/og-image.png'],
    creator: '@my_twitter',
  },
  icons: {
    icon: '/favicon.ico',
    shortcut: '/favicon-16x16.png',
    apple: '/apple-touch-icon.png',
  },
  robots: 'index, follow',
  themeColor: '#ff6600',
};

Всё это — статическая генерация: если вы экспортируете объект, Next.js "запекает" эти метаданные прямо в итоговый HTML при сборке. Это быстро и эффективно!

3. Как работает иерархия metadata в App Router

В Next.js 15 (и App Router вообще) метаданные наследуются по структуре папок:

  • Метаданные из layout.tsx применяются ко всем вложенным страницам.
  • Если страница экспортирует свои собственные метаданные, они переопределяют или дополняют layout'ные.
  • Если какое-то поле не указано на уровне страницы, берётся из ближайшего layout'а выше по иерархии.

Пример


app/
  layout.tsx         // title: 'Мой сайт'
  page.tsx           // title: 'Главная страница'
  about/
    layout.tsx       // title: 'О компании'
    page.tsx         // title: 'О нас'
    team/
      page.tsx       // title: 'Команда'
  • На / будет title "Главная страница".
  • На /about — "О нас".
  • На /about/team — "Команда".
  • Если бы в about/team/page.tsx не было своего title, взялся бы из ближайшего layout — "О компании".

Это позволяет централизованно задавать "шапку" для всего раздела, а страницы могут переопределять только нужные поля.

4. Практика: добавляем метаданные в ваше учебное приложение

Давайте продолжим развивать ваше учебное приложение (например, сайт заметок или блог), которое мы строим с самого начала модуля Next.js.

Шаг 1. Добавим базовые метаданные в корневой layout


// app/layout.tsx
export const metadata = {
  title: 'Мой учебный блог',
  description: 'Блог о программировании на Next.js и не только',
  openGraph: {
    title: 'Мой учебный блог',
    description: 'Блог о программировании на Next.js и не только',
    url: 'https://my-blog.ru',
    siteName: 'MyBlog',
    images: [
      {
        url: 'https://my-blog.ru/og-image.png',
        width: 1200,
        height: 630,
        alt: 'Обложка блога',
      },
    ],
    locale: 'ru_RU',
    type: 'website',
  },
  icons: {
    icon: '/favicon.ico',
  },
};

Теперь все страницы по умолчанию будут иметь эти метаданные.

Шаг 2. Переопределим title и description на главной странице


// app/page.tsx
export const metadata = {
  title: 'Главная страница — Мой учебный блог',
  description: 'Читайте свежие статьи по фронтенду, Next.js и React!',
};

Шаг 3. Для страницы отдельной заметки (например, /posts/hello-nextjs)


// app/posts/hello-nextjs/page.tsx
export const metadata = {
  title: 'Hello Next.js — первая статья',
  description: 'Разбираемся с основами Next.js 15 и App Router на практике.',
  openGraph: {
    title: 'Hello Next.js — первая статья',
    description: 'Разбираемся с основами Next.js 15 и App Router на практике.',
    images: [
      {
        url: 'https://my-blog.ru/posts/hello-nextjs/cover.png',
        width: 1200,
        height: 630,
        alt: 'Обложка статьи Hello Next.js',
      },
    ],
  },
};

Теперь если вы расшарите ссылку на эту статью в Telegram или Facebook, появится красивая карточка с нужным заголовком, описанием и картинкой (если, конечно, картинка реально доступна по этому URL).

5. Полезные нюансы

Как добавить favicon и другие иконки

В App Router и Metadata API можно централизованно задавать иконки для сайта.

Пример

// app/layout.tsx или app/page.tsx
export const metadata = {
  // ...
  icons: {
    icon: '/favicon.ico',
    shortcut: '/favicon-16x16.png',
    apple: '/apple-touch-icon.png',
  },
};

Пояснение:

  • icon: стандартный favicon для большинства браузеров.
  • shortcut: иконка для закладок.
  • apple: иконка для добавления сайта на главный экран iOS.

Иконки должны лежать в папке public/ вашего проекта.

Использование metadata в layout.tsx и page.tsx: что где задавать

  • layout.tsx — задаём общие метаданные для всего раздела или сайта (например, название сайта, глобальное описание, favicon, базовые Open Graph).
  • page.tsx — задаём метаданные для конкретной страницы (например, уникальный title, description, картинку для статьи).

Важно: Если какое-то поле не указано на уровне страницы, оно берётся из layout'а!

Как проверить, что метаданные работают

  • Откройте страницу в браузере, нажмите F12 (DevTools) → Elements → Head
    Проверьте, что в <head> появились нужные <title>, <meta name="description">, Open Graph и т.д.
  • Используйте валидаторы:
  • Проверьте favicon: Должен появиться на вкладке браузера.

6. Типичные ошибки при статической генерации метаданных

Ошибка №1: metadata не экспортируется или экспортируется не из того файла.
Если вы забыли экспортировать объект metadata или сделали это в неправильном файле (например, в компоненте, а не в page.tsx или layout.tsx), Next.js проигнорирует ваши метаданные. Всегда экспортируйте из верхнего уровня файла страницы или layout'а.

Ошибка №2: неправильная структура объекта metadata.
Если вы перепутали поля (например, написали desription вместо description или забыли вложить поля в openGraph), Next.js не сможет корректно сгенерировать нужные метатеги. Используйте автодополнение IDE и официальную документацию.

Ошибка №3: забыли добавить картинки для Open Graph или указали несуществующий URL.
Если вы указали путь к картинке, которой нет в папке public, карточка в соцсетях будет без картинки или с ошибкой. Проверьте, что все файлы реально существуют.

Ошибка №4: конфликты между layout и page metadata.
Если вы задали одинаковые поля и в layout, и в page, страница переопределит layout. Это ожидаемое поведение, но иногда приводит к неожиданным результатам, если вы этого не учли.

Ошибка №5: забыли обновить метаданные после копирования файла.
Очень частая ситуация: вы копируете page.tsx для новой страницы, а metadata оставляете прежней. В результате все страницы имеют одинаковый title и description. Не забудьте изменить эти поля!

Ошибка №6: использование устаревшего подхода с <Head> в App Router.
В App Router не нужно (и не стоит) использовать next/head и ручное добавление метатегов. Всё делается через metadata!

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ