Как добавить метаданные и создавать OG-изображения
API метаданных можно использовать для определения метаданных вашего приложения с целью улучшения SEO и возможности расшаривания в интернете. Доступны следующие варианты:
- Статический объект
metadata
- Динамическая функция
generateMetadata
- Специальные конвенции файлов для добавления статических или динамически генерируемых фавиконов и OG-изображений.
Со всеми перечисленными вариантами Next.js автоматически сгенерирует соответствующие теги <head>
для вашей страницы, которые можно проверить в инструментах разработчика браузера.
Стандартные поля
Есть два стандартных тега meta
, которые всегда добавляются, даже если маршрут не определяет метаданные:
- Тег meta charset устанавливает кодировку символов для сайта.
- Тег meta viewport задает ширину и масштаб области просмотра для адаптации под разные устройства.
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
Остальные поля метаданных можно определить с помощью объекта Metadata
(для статических метаданных) или функции generateMetadata
(для генерируемых метаданных).
Статические метаданные
Чтобы определить статические метаданные, экспортируйте объект 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() {}
Полный список доступных опций можно найти в документации generateMetadata
.
Генерируемые метаданные
Функция generateMetadata
позволяет получать метаданные, зависящие от данных. Например, чтобы получить заголовок и описание для конкретной записи блога:
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = (await params).slug
// получаем информацию о записи
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
const slug = (await params).slug
// получаем информацию о записи
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }) {}
Под капотом Next.js будет стримить метаданные отдельно от UI и вставлять их в HTML, как только они будут разрешены.
Мемоизация запросов данных
В некоторых случаях может потребоваться получать одни и те же данные как для метаданных, так и для самой страницы. Чтобы избежать дублирования запросов, можно использовать функцию React cache
для мемоизации возвращаемого значения и выполнения запроса только один раз. Например, чтобы получить информацию о записи блога и для метаданных, и для страницы:
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost будет использоваться дважды, но выполнится только один раз
export const getPost = cache(async (slug: string) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost будет использоваться дважды, но выполнится только один раз
export const getPost = cache(async (slug) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
import { getPost } from '@/app/lib/data'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
import { getPost } from '@/app/lib/data'
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
Файловые метаданные
Для метаданных доступны следующие специальные файлы:
- favicon.ico, apple-icon.jpg и icon.jpg
- opengraph-image.jpg и twitter-image.jpg
- robots.txt
- sitemap.xml
Их можно использовать для статических метаданных или генерировать программно.
Фавиконы
Фавиконы — это маленькие иконки, представляющие ваш сайт в закладках и результатах поиска. Чтобы добавить фавикон в приложение, создайте файл favicon.ico
в корневой папке приложения.

Фавиконы также можно генерировать программно. Подробнее см. в документации по фавиконам.
Статические Open Graph изображения
Open Graph (OG) изображения представляют ваш сайт в социальных сетях. Чтобы добавить статическое OG-изображение в приложение, создайте файл opengraph-image.png
в корневой папке приложения.

Также можно добавить OG-изображения для конкретных маршрутов, создав файл opengraph-image.png
глубже в структуре папок. Например, чтобы создать OG-изображение для маршрута /blog
, добавьте файл opengraph-image.jpg
в папку blog
.

Более специфичное изображение будет иметь приоритет над любыми OG-изображениями выше в структуре папок.
Также поддерживаются другие форматы изображений, такие как
jpeg
,png
иwebp
. Подробнее см. в документации по Open Graph изображениям.
Генерируемые Open Graph изображения
Конструктор ImageResponse
позволяет генерировать динамические изображения с использованием JSX и CSS. Это полезно для OG-изображений, зависящих от данных.
Например, чтобы сгенерировать уникальное OG-изображение для каждой записи блога, добавьте файл opengraph-image.ts
в папку blog
и импортируйте конструктор ImageResponse
из next/og
:
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
// Метаданные изображения
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Генерация изображения
export default async function Image({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return new ImageResponse(
(
// Элемент JSX для ImageResponse
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
)
)
}
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
// Метаданные изображения
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Генерация изображения
export default async function Image({ params }) {
const post = await getPost(params.slug)
return new ImageResponse(
(
// Элемент JSX для ImageResponse
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
)
)
}
ImageResponse
поддерживает общие CSS-свойства, включая flexbox и абсолютное позиционирование, пользовательские шрифты, перенос текста, центрирование и вложенные изображения. Полный список поддерживаемых CSS-свойств.
Полезно знать:
- Примеры доступны в Vercel OG Playground.
ImageResponse
использует@vercel/og
,satori
иresvg
для преобразования HTML и CSS в PNG.- Поддерживаются только flexbox и подмножество CSS-свойств. Сложные макеты (например,
display: grid
) работать не будут.