Cookie Consent для Next.js, Gatsby та статичних сайтів: посібник з інтеграції для розробників
Проблема з consent на статичних сайтах
Сучасні JavaScript‑фреймворки на кшталт Next.js, Gatsby та Nuxt.js запровадили зміну парадигми в тому, як сторінки будуються та доставляються. Сторінки попередньо рендеряться під час збірки або на сервері, а потім гідруються на клієнті. Це створює унікальний виклик для cookie consent: банер згоди має бути готовим до виконання будь‑яких трекінгових скриптів, але сама сторінка може вже бути відрендерена та закешована на edge‑рівні.
Традиційні CMP створювалися для серверно‑рендерених PHP або простих HTML‑сторінок, де документ завантажується лінійно зверху вниз. У світі фреймворків з code splitting, лінивим завантаженням та потоковим серверним рендерингом ці припущення руйнуються. Щоб коректно реалізувати consent у таких середовищах, потрібно розуміти конвеєр рендерингу.
Чому таймінг важливіший, ніж здається
У стандартній HTML‑сторінці розмістити CMP‑скрипт у <head> перед іншими скриптами просто. У Next.js App Router або Gatsby ситуація складніша:
- Спочатку приходить попередньо згенерований HTML: Браузер отримує повний HTML з CDN або сервера. Якщо в цьому HTML є inline‑скрипти чи сторонні теги, вони можуть виконатися до того, як завантажиться ваша логіка згоди.
- Проміжок до гідрації: Гідрація React відбувається після того, як HTML намальовано. Якщо ваш компонент згоди — це React‑компонент, він не існує у функціональному стані, доки гідрація не завершиться. У цей проміжок Google‑теги або аналітичні скрипти можуть спрацювати без згоди.
- Ускладнення через кешування на edge‑рівні: Якщо ви використовуєте ISR (Incremental Static Regeneration) або edge‑функції, HTML кешується. Ви не можете динамічно інжектити логіку, що залежить від згоди, у кешований HTML без клієнтського механізму.
Ключовий принцип такий: згода має встановлюватися на рівні скриптів, а не компонентів. React‑компонент, який рендерить банер згоди, з’являється надто пізно, якщо стає інтерактивним лише після гідрації.
Інтеграція з Next.js App Router
Next.js 13+ з App Router запровадив новий спосіб роботи зі скриптами. Ось рекомендований підхід до інтеграції consent:
Крок 1: Завантажте CMP‑скрипт у кореневому layout
Використовуйте компонент Next.js Script зі стратегією beforeInteractive у вашому кореневому layout.tsx. Це вказує Next.js інжектити скрипт в початковий HTML‑документ до початку гідрації:
Стратегія beforeInteractive є критичною. Стратегія за замовчуванням afterInteractive завантажує скрипти після гідрації, що надто пізно для consent. З beforeInteractive CMP‑скрипт включається в серверно‑рендерений HTML і виконується під час завантаження сторінки.
Крок 2: Встановіть значення згоди за замовчуванням до Google‑тегів
Перед вашим фрагментом Google Tag Manager або gtag.js додайте inline‑скрипт, який встановлює стани згоди за замовчуванням. Це гарантує, що навіть якщо GTM завантажиться до появи CMP‑банера, він дотримуватиметься відхилених значень за замовчуванням:
Цей inline‑скрипт слід розмістити в <head> вашого кореневого layout, перед CMP‑ та GTM‑скриптами. У Next.js ви можете використати звичайний тег <script> всередині елемента <head> вашого layout для цієї мети.
Крок 3: Обробляйте зміни маршрутів
У навігації типу single‑page CMP‑скрипт завантажується один раз, але зміни маршрутів не спричиняють повне перезавантаження сторінки. Ваш CMP має зберігатися між клієнтськими переходами. FlexyConsent робить це автоматично — після завантаження він залишається активним на всіх маршрутах без повторної ініціалізації.
Інтеграція з Next.js Pages Router
Для проєктів, які все ще ��икористовують Pages Router, підхід подібний, але замість кореневого layout використовується _document.tsx. Розмістіть CMP‑скрипт у компоненті <Head> вашого кастомного класу Document. Стратегія beforeInteractive працює так само в Pages Router.
Ключова відмінність у тому, що _document.tsx рендериться лише на сервері, тож будь‑яка логіка згоди тут гарантовано потрапляє в початковий HTML‑пейлоад.
Інтеграція статичного сайту Gatsby
Gatsby генерує повністю статичний HTML під час збірки. Немає серверного рендерингу під час запиту, що спрощує одні аспекти, але ускладнює інші:
- Використовуйте
gatsby-ssr.tsx, щоб інжектити CMP‑скрипт у<head>кожної сторінки. APIonRenderBodyдозволяє додавати скрипти в head, які будуть присутні в кожно��у статичному HTML‑файлі. - Уникайте Gatsby‑плагінів, що ліниво завантажують consent: Деякі плагіни спільноти обгортають consent у React‑компоненти, які монтуються лише після гідрації. Це створює описаний вище часовий розрив.
- Розміщуйте значення згоди за замовчуванням inline: Використовуйте
setHeadComponentsуgatsby-ssr.tsx, щоб додати inline‑скрипт, який встановлює стани згоди за замовчуванням. Цей скрипт буде в статичному HTML і виконається негайно.
Підхід Gatsby на етапі збірки означає, що кожен HTML‑файл на вашому CDN міститиме скрипт згоди. Це насправді ідеально — немає серверної логіки, яка може зламатися або некоректно кешуватися.
Особливості Nuxt.js
Nuxt.js (на базі Vue) має власні патерни. У Nuxt 3 використовуйте composable useHead або конфігурацію app head у nuxt.config.ts, щоб додати CMP‑скрипт глобально. Nuxt підтримує опцію body: false (розміщує скрипти в head) та атрибут async для неблокувального завантаження.
Для режиму серверного рендерингу Nuxt діє те саме правило: CMP‑скрипт має бути в початковій HTML‑відповіді, а не динамічно інжектитися Vue‑компонентом після mount.
Як уникнути зсуву макета
Банери згоди відомі тим, що спричиняють Cumulative Layout Shift (CLS) — Core Web Vital, який впливає на SEO‑рейтинги. Коли банер з’являється після рендерингу сторінки, він зсуває контент вниз або неочікувано перекриває його.
Стратегії мінімізації CLS від банерів згоди:
- Використовуйте банер, розташований внизу: Банери внизу вікна перегляду не зсувають контент сторінки. Це найкращий підхід з точки зору CLS.
- Резервуйте простір: Якщо вам потрібен верхній банер, зарезервуйте вертикальний простір у CSS, щоб макет сторінки враховував банер ще до його рендерингу.
- Уникайте модальних оверлеїв при завантаженні: Повноекранні стіни згоди, які з’являються після рендерингу сторінки, створюють відчутну нестабільність макета. Якщо вам потрібна така стіна, рендерте її як частину початкового стану сторінки.
- Завантажуйте CMP синхронно в head: Коли CMP завантажується як блокувальний рендеринг скрипт у head, банер може з’явитися як частина початкового відмалювання, а не «вистрибувати» пізніше.
Фреймворк‑агностичний підхід FlexyConsent
FlexyConsent створено для роботи з будь‑яким фреймворком — або взагалі без фреймворку — завдяки роботі на рівні скриптів, а не компонентів. Ос�� чому це важливо:
- Один асинхронний тег скрипта: Достатньо одного тега
<script>у<head>. Не потрібно встановлювати npm‑пакети, фреймворк‑специфічні обгортки чи налаштовувати збірку. - Значення згоди за замовчуванням спрацьовують миттєво: Скрипт встановлює значення Consent Mode V2 за замовчуванням як першу дію, до будь‑яких callback чи маніпуляцій DOM. Це означає, що Google‑теги дотримуються згоди з першої мілісекунди.
- Відсутність залежності від DOM: Логіка згоди не чекає гідрації React, Vue чи Svelte. Вона працює незалежно від життєвого циклу фреймворку.
- Працює з SSG, SSR, ISR та CSR: Оскільки це звичайний скрипт, він однаково функціонує, незалежно від того, чи сторінка була статично згенерована, серв��рно‑рендерена, інкрементально регенерована чи рендерена на клієнті.
Порада для розробників: Найпростіший тест коректної інтеграції CMP — відкрити вкладку Network у браузері, відфільтрувати запити за доменами Google і перезавантажити сторінку. Жодні запити до Google не повинні виконуватися до того, як у консолі з’явиться команда встановлення значень згоди за замовчуванням. Якщо вони з’являються раніше, ваш CMP завантажується надто пізно.
Безкоштовний план FlexyConsent підтримує необмежену кількість переглядів сторінок і працює з Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix та звичайним HTML. Інтеграція однакова для всіх: один тег скрипта, правильно розміщений.