В архітектурі сучасних веб-додатків пошукова оптимізація безпосередньо залежить від механізму доставки метаданих пошуковим краулерам (Googlebot, Bingbot, YandexBot) та AI-індексаторам (GPTBot, PerplexityBot). Класичні пошукові роботи функціонують за моделлю Web Rendering Service (WRS), що реалізує двофазну індексацію:
<title>, <meta name="description">, директиви <meta name="robots"> та канонічні посилання парсяться миттєво з мінімальними витратами обчислювальних ресурсів (Crawl Budget).В епоху Pages Router або при використанні клієнтських бібліотек на кшталт react-helmet маніпуляції з <head> виконувалися через DOM-мутації на клієнті після гідратації. Якщо краулер не дочікувався виконання скриптів або завершував обхід на першій фазі, пошуковий індекс отримував порожні теги або дефолтні заглушки.
Next.js 15 усуває цю архітектурну ваду на рівні специфікації React Server Components (RSC) та вбудованого Metadata API. Метадані оголошуються декларативно на сервері, компілюються в єдине дерево параметрів для всього маршруту та інжектуються безпосередньо в стрімінговий SSR-шел початкової HTML-відповіді до передачі першого байта клієнту. Інтеграція з механізмом React 19 Document Metadata Hoisting гарантує, що теги <title>, <meta> та <link> автоматично піднімаються в секцію <head> документа без гідратаційних колізій.
Metadata API в Next.js 15 оперує концепцією ієрархічного дерева сегментів маршруту (Route Segments). Метадані обчислюються зверху вниз: від кореневого макета app/layout.tsx через вкладені макети layout.tsx у групах маршрутів до кінцевого вузла — сторінки page.tsx.
Злиття метаданих між рівнями маршрутизації підпорядковується правилу Shallow Merge: об'єкти верхнього рівня перезаписуються цілком, за винятком спеціальних полів (наприклад, title.template та openGraph.images).
+-------------------------------------------------------------------------+
| app/layout.tsx |
| metadataBase: https://agenticlab.dev |
| title: { default: 'Agentic Lab', template: '%s | Agentic Lab' } |
| robots: { index: true, follow: true } |
| openGraph: { siteName: 'Agentic Lab Platform', locale: 'uk_UA' } |
+-------------------------------------------------------------------------+
│
▼ (Каскадне успадкування)
+-------------------------------------------------------------------------+
| app/(dashboard)/layout.tsx |
| robots: { index: false, follow: false } <-- Перезапис директиви |
+-------------------------------------------------------------------------+
│
▼ (Поверхневе злиття)
+-------------------------------------------------------------------------+
| app/(dashboard)/settings/page.tsx |
| title: 'Налаштування профілю' |
+-------------------------------------------------------------------------+
│
▼ (Компіляція фінального HTML SSR)
+-------------------------------------------------------------------------+
| <head> в HTML |
| <title>Налаштування профілю | Agentic Lab</title> |
| <meta name="robots" content="noindex, nofollow" /> |
| <meta property="og:site_name" content="Agentic Lab Platform" /> |
+-------------------------------------------------------------------------+layout.tsx з автоматичною верифікацією пошукових консолей (Google Search Console, Bing Webmaster) та єдиним шаблоном мета-заголовків.robots: { index: false, follow: false } на рівні проміжних макетів без дублювання коду на сторінках.metadataBase.max-snippet, max-image-preview: large).Статичні метадані визначаються за допомогою іменованого експорту об'єкта metadata з типом Metadata з пакета 'next'.
app/layout.tsx)#У кореневому макеті оголошуються фундаментальні директиви сайту: metadataBase, дефолтний title із шаблоном, глобальний OpenGraph, фавікони та ключі верифікації власника домену.
import type { Metadata, Viewport } from 'next';
// Viewport винесено в окремий експорт згідно зі специфікацією Next.js 15
export const viewport: Viewport = {
themeColor: '#09090b',
width: 'device-width',
initialScale: 1,
};
export const metadata: Metadata = {
// metadataBase обов'язковий для генерації абсолютних URL в OG-тегах та canonical
metadataBase: new URL(
process.env.NEXT_PUBLIC_APP_URL || 'https://agenticlab.dev'
),
title: {
default: 'Fullstack Agentic Lab — Інженерна платформа веб-розробки',
template: '%s | Fullstack Agentic Lab',
},
description: 'Глибокі інтерактивні курси та спеціалізації з Next.js 15, React 19, архітектури та веб-продуктивності.',
applicationName: 'Fullstack Agentic Lab Platform',
authors: [{ name: 'Fullstack Agentic Lab Team', url: 'https://agenticlab.dev' }],
generator: 'Next.js 15',
keywords: ['Next.js 15', 'React 19', 'SEO', 'Web Performance', 'Metadata API'],
referrer: 'origin-when-cross-origin',
// Пошукові директиви для глобального краулінгу
robots: {
index: true,
follow: true,
nocache: false,
googleBot: {
index: true,
follow: true,
noimageindex: false,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
// OpenGraph протокол для соцмереж та месенджерів
openGraph: {
type: 'website',
locale: 'uk_UA',
url: 'https://agenticlab.dev',
siteName: 'Fullstack Agentic Lab Platform',
title: 'Fullstack Agentic Lab — Інженерна платформа веб-розробки',
description: 'Інтерактивне навчання Fullstack-розробці та системній архітектурі.',
images: [
{
url: '/og-default.png', // Автоматично доповниться до абсолютного URL через metadataBase
width: 1200,
height: 630,
alt: 'Fullstack Agentic Lab — Modern Web Architecture',
},
],
},
// Картки Twitter / X
twitter: {
card: 'summary_large_image',
title: 'Fullstack Agentic Lab — Інженерна платформа веб-розробки',
description: 'Інтерактивне навчання Fullstack-розробці та системній архітектурі.',
creator: '@agenticlab_dev',
images: ['/og-default.png'],
},
// Верифікація прав у вебмайстрах
verification: {
google: 'google-site-verification-token-hash-2026',
yandex: 'yandex-verification-token-2026',
},
// Іконки та фавікони
icons: {
icon: [
{ url: '/favicon.ico' },
{ url: '/icon.svg', type: 'image/svg+xml' },
],
apple: [
{ url: '/apple-touch-icon.png', sizes: '180x180' },
],
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="uk">
<body className="bg-bg-surface text-text-primary antialiased">
{children}
</body>
</html>
);
}app/(private)/layout.tsx)#Якщо необхідно виключити з пошукової видачі цілу групу маршрутів (наприклад, особистий кабінет користувача, білінг або адміністративну панель), директива robots перевизначається у відповідному макеті:
import type { Metadata } from 'next';
export const metadata: Metadata = {
// Повна заборона індексації всіх дочірніх маршрутів всередині (private)
robots: {
index: false,
follow: false,
googleBot: {
index: false,
follow: false,
},
},
// Заборона формування прев'ю в картках соцмереж для приватного контенту
openGraph: {
images: [],
},
};
export default function PrivateLayout({
children,
}: {
children: React.ReactNode;
}) {
return <div className="dashboard-container">{children}</div>;
}app/about/page.tsx)#На рівні кінцевої сторінки розробник вказує специфічний заголовок (який автоматично інтерполюється в шаблон батька), канонічний URL та індивідуальний опис:
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Про платформу', // Перетворюється на: "Про платформу | Fullstack Agentic Lab"
description: 'Архітектурні принципи, стандарти якості та місія інженерного проєкту Fullstack Agentic Lab.',
alternates: {
canonical: '/about', // Резолвиться в: https://agenticlab.dev/about
},
openGraph: {
title: 'Про платформу | Fullstack Agentic Lab',
description: 'Інженерні стандарти та методологія навчання сучасних розробників.',
url: '/about',
images: [
{
url: '/images/og-about.png',
width: 1200,
height: 630,
alt: 'Fullstack Agentic Lab Architecture Overview',
},
],
},
};
export default function AboutPage() {
return (
<main className="max-w-4xl mx-auto py-12">
<h1 className="text-3xl font-bold">Про платформу Fullstack Agentic Lab</h1>
<p className="mt-4 text-text-muted">
Ми проєктуємо освітні програми для інженерів Middle+ та Senior рівня.
</p>
</main>
);
}Якщо в metadata вказані відносні шляхи (openGraph.images: ['/og.png'], alternates.canonical: '/blog'), але властивість metadataBase не задана в жодному з батьківських макетів, Next.js під час збирання видасть попередження:
metadata.metadataBase is not set for resolving social open graph or twitter images.
У результаті фреймворк відкотиться до http://localhost:3000, і пошукові краулери отримають посилання на локалхост замість робочого продакшен-домену. Завжди конфігуруйте metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL) в app/layout.tsx.
Next.js виконує поверхневе злиття (shallow merge) об'єктів першого рівня. Якщо в app/layout.tsx оголошено складний об'єкт:
openGraph: { siteName: 'Fullstack Agentic Lab', locale: 'uk_UA', type: 'website' },
а на сторінці page.tsx вказано об'єкт:
openGraph: { title: 'Стаття 1', url: '/article-1' },
то властивості siteName і locale будуть повністю затерті на цій сторінці! Для збереження глобальних атрибутів OpenGraph при статичному експорті їх необхідно явно повторно передавати або використовувати динамічний метод generateMetadata з аргументом parent.
Властивість title.template застосовується тільки до дочірніх сегментів маршруту. Якщо в app/layout.tsx задано шаблон template: '%s | Fullstack Agentic Lab', він не форматує заголовок сторінки app/page.tsx. Для кореневої сторінки відобразиться значення title.default. Якщо ж на вкладеній сторінці потрібно проігнорувати шаблон і вивести строго фіксований заголовок, використовуйте конструкцію title: { absolute: 'Спеціальний заголовок без суфікса' }.
Metadata API спроєктований виключно для Server Components. Спроба оголосити export const metadata: Metadata всередині файлу з директивою 'use client' призведе до помилки збирання Next.js:
You are attempting to export "metadata" from a component marked with "use client", which is disallowed.
Якщо сторінці потрібен інтерактивний клієнтський хук, винесіть логіку клієнтського інтерфейсу в дочірній компонент, зберігши саму сторінку page.tsx серверним компонентом.
Сервер Next.js визначає стек сегментів для вхідного запиту: RootLayout -> GroupLayout -> LeafPage.
Виконується збирання експортованих об'єктів metadata на кожному рівні маршруту зверху вниз.
Фреймворк виконує поверхневе злиття полів. Дочірній title підставляється в батьківський title.template.
Усі відносні шляхи для OpenGraph, Twitter, favicon та canonical перетворюються на абсолютні валідні URL.
Сформовані теги <title>, <meta>, <link> вбудовуються в стартовий стрім відповіді в тег <head> до гідратації.