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 | Ключевые слова (редко используется) | |
| authors | Авторы страницы | |
| openGraph | Open Graph-метаданные для соцсетей | |
| Twitter Cards | |
|
| icons | Favicon и другие иконки | |
| robots | Инструкции для поисковых роботов | |
| themeColor | Цвет темы для мобильных браузеров | |
Пример расширенного объекта 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!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ