Zgoda na pliki cookie w Next.js, Gatsby i statycznych stronach: przewodnik integracji dla deweloperów
Problem zgody na statycznych stronach
Nowoczesne frameworki JavaScript, takie jak Next.js, Gatsby i Nuxt.js, wprowadziły zmianę paradygmatu w sposobie budowania i dostarczania stron internetowych. Strony są wstępnie renderowane w czasie budowania lub na serwerze, a następnie hydratowane po stronie klienta. Tworzy to unikalne wyzwanie dla zgody na pliki cookie: baner zgody musi być gotowy, zanim jakiekolwiek skrypty śledzące zostaną uruchomione, ale sama strona może być już wyrenderowana i zcache’owana na brzegu (edge).
Tradycyjne CMP były projektowane z myślą o stronach renderowanych po stronie serwera w PHP lub prostych stronach HTML, gdzie dokument ładuje się liniowo od góry do dołu. W świecie frameworków z dzieleniem kodu, leniwym ładowaniem i strumieniowym renderowaniem po stronie serwera te założenia przestają działać. Poprawne wdrożenie zgody w takich środowiskach wymaga zrozumienia całego potoku renderowania.
Dlaczego timing ma większe znaczenie, niż się wydaje
W standardowej stronie HTML umieszczenie skryptu CMP w <head> przed innymi skryptami jest proste. W Next.js App Router lub Gatsby sytuacja jest bardziej złożona:
- Najpierw przychodzi wstępnie wyrenderowany HTML: Przeglądarka otrzymuje kompletne HTML z CDN lub serwera. Jeśli w tym HTML znajdują się skrypty inline lub tagi zewnętrzne, mogą one wykonać się, zanim załaduje się logika zgody.
- Luka hydratacyjna: Hydratacja Reacta następuje po wyrenderowaniu HTML na ekranie. Jeśli Twój komponent zgody jest komponentem React, nie istnieje on w stanie funkcjonalnym, dopóki hydratacja się nie zakończy. W tej luce tagi Google lub skrypty analityczne mogą odpalić bez zgody.
- Komplikacje związane z cache’owaniem na brzegu: Jeśli używasz ISR (Incremental Static Regeneration) lub funkcji edge, HTML jest cache’owany. Nie możesz dynamicznie wstrzykiwać logiki zależnej od zgody do zcache’owanego HTML bez mechanizmu po stronie klienta.
Kluczowa zasada jest taka: zgoda musi być ustalana na poziomie skryptu, a nie komponentu. Komponent React renderujący baner zgody pojawia się zbyt późno, jeśli staje się interaktywny dopiero po hydratacji.
Integracja z Next.js App Router
Next.js 13+ z App Router wprowadził nowy sposób obsługi skryptów. Oto zalecane podejście do integracji zgody:
Krok 1: Załaduj skrypt CMP w głównym layoucie
Użyj komponentu Script Next.js ze strategią beforeInteractive w głównym pliku layout.tsx. Informuje to Next.js, aby wstrzyknąć skrypt do początkowego dokumentu HTML, zanim rozpocznie się hydratacja:
Strategia beforeInteractive jest krytyczna. Domyślna strategia afterInteractive ładuje skrypty po hydratacji, co jest zbyt późno dla zgody. Dzięki beforeInteractive skrypt CMP jest dołączony do HTML renderowanego po stronie serwera i wykonuje się podczas ładowania strony.
Krok 2: Ustaw domyślne stany zgody przed tagami Google
Przed snippetem Google Tag Manager lub gtag.js umieść skrypt inline, który ustawia domyślne stany zgody. Zapewnia to, że nawet jeśli GTM załaduje się, zanim pojawi się baner CMP, będzie respektował domyślne ustawienia odmowy:
Ten skrypt inline powinien być umieszczony w <head> głównego layoutu, przed skryptami CMP i GTM. W Next.js możesz w tym celu użyć zwykłego tagu <script> wewnątrz elementu <head> w layoucie.
Krok 3: Obsłuż zmiany tras
W nawigacji typu single-page application skrypt CMP ładuje się raz, ale zmiany tras nie wywołują pełnego przeładowania strony. Twoje CMP musi utrzymywać się pomiędzy nawigacjami po stronie klienta. FlexyConsent obsługuje to automatycznie — po załadowaniu pozostaje aktywny przy wszystkich zmianach tras bez ponownej inicjalizacji.
Integracja z Next.js Pages Router
Dla projektów wciąż używających Pages Router podejście jest podobne, ale wykorzystuje _document.tsx zamiast głównego layoutu. Umieść skrypt CMP w komponencie <Head> własnej klasy Document. Strategia beforeInteractive działa w Pages Router w ten sam sposób.
Kluczowa różnica polega na tym, że _document.tsx renderuje się wyłącznie po stronie serwera, więc każda logika zgody umieszczona tutaj jest gwarantowanie obecna w początkowym payloadzie HTML.
Integracja statycznych stron w Gatsby
Gatsby generuje w pełni statyczny HTML w czasie budowania. Nie ma renderowania po stronie serwera w momencie żądania, co upraszcza niektóre aspekty, ale komplikuje inne:
- Użyj
gatsby-ssr.tsx, aby wstrzyknąć skrypt CMP do<head>każdej strony. APIonRenderBodypozwala dodać skrypty do sekcji head, które będą obecne w każdym statycznym pliku HTML. - Unikaj wtyczek Gatsby, które leniwie ładują zgodę: Niektóre wtyczki społeczności opakowują zgodę w komponenty React, które montują się dopiero po hydratacji. Tworzy to opisaną wcześniej lukę czasową.
- Umieszczaj domyślne ustawienia zgody inline: Użyj
setHeadComponentswgatsby-ssr.tsx, aby dodać skrypt inline ustawiający domyślne stany zgody. Ten skrypt znajdzie się w statycznym HTML i wykona się natychmiast.
Podejście build-time w Gatsby oznacza, że każdy plik HTML na Twoim CDN będzie zawierał skrypt zgody. To w rzeczywistości idealne rozwiązanie — nie ma logiki serwerowej, która mogłaby zawieść lub zostać niepoprawnie zcache’owana.
Uwagi dotyczące Nuxt.js
Nuxt.js (oparty na Vue) ma własne wzorce. W Nuxt 3 użyj kompozycji useHead lub konfiguracji nagłówka aplikacji w nuxt.config.ts, aby dodać skrypt CMP globalnie. Nuxt obsługuje opcję body: false (umieszczającą skrypty w head) oraz atrybut async dla nieblokującego ładowania.
Dla trybu renderowania po stronie serwera w Nuxt obowiązuje ta sama zasada: skrypt CMP musi znajdować się w początkowej odpowiedzi HTML, a nie być dynamicznie wstrzykiwany przez komponent Vue po zamontowaniu.
Unikanie przesunięć layoutu
Banery zgody są notoryczne pod względem powodowania Cumulative Layout Shift (CLS), wskaźnika Core Web Vitals wpływającego na pozycje w SEO. Gdy baner pojawia się po wyrenderowaniu strony, spycha treść w dół lub niespodziewanie ją przykrywa.
Strategie minimalizowania CLS powodowanego przez banery zgody:
- Używaj banera na dole ekranu: Banery u dołu viewportu nie przesuwają treści strony. To najbardziej przyjazne dla CLS podejście.
- Rezerwuj miejsce: Jeśli musisz użyć banera u góry, zarezerwuj pionową przestrzeń w CSS, aby layout strony uwzględniał baner jeszcze przed jego wyrenderowaniem.
- Unikaj modalnych overlayów przy ładowaniu: Pełnoekranowe ściany zgody, które pojawiają się po wyrenderowaniu strony, powodują odczuwalną niestabilność layoutu. Jeśli potrzebujesz takiej ściany, renderuj ją jako część początkowego stanu strony.
- Ładuj CMP synchronicznie w head: Gdy CMP jest ładowany jako blokujący render skrypt w sekcji head, baner może pojawić się w ramach początkowego malowania, zamiast wyskakiwać później.
Framework-agnostyczne podejście FlexyConsent
FlexyConsent został zaprojektowany tak, aby działał z dowolnym frameworkiem — lub bez żadnego frameworka — poprzez operowanie na poziomie skryptu, a nie komponentu. Oto dlaczego ma to znaczenie:
- Pojedynczy asynchroniczny tag skryptu: Wystarczy jeden tag
<script>w<head>. Bez instalowania paczek npm, bez wrapperów specyficznych dla frameworka, bez konfiguracji builda. - Domyślne ustawienia zgody uruchamiają się natychmiast: Skrypt jako pierwszą akcję ustawia domyślne wartości Consent Mode V2, zanim wykona jakiekolwiek callbacki czy manipulacje DOM. Oznacza to, że tagi Google respektują zgodę od pierwszej milisekundy.
- Brak zależności od DOM: Logika zgody nie czeka na hydratację React, Vue czy Svelte. Działa niezależnie od cyklu życia frameworka.
- Działa z SSG, SSR, ISR i CSR: Ponieważ jest zwykłym skryptem, funkcjonuje identycznie niezależnie od tego, czy strona została wygenerowana statycznie, wyrenderowana na serwerze, regenerowana inkrementalnie czy renderowana po stronie klienta.
Porada dla deweloperów: Najprostszy test poprawnej integracji CMP to otwarcie zakładki Network w przeglądarce, przefiltrowanie po domenach Google i przeładowanie strony. Żadne żądania do Google nie powinny zostać wysłane przed pojawieniem się w konsoli komendy ustawiającej domyślne wartości zgody. Jeśli się pojawiają, Twoje CMP ładuje się zbyt późno.
Darmowy plan FlexyConsent obsługuje nielimitowaną liczbę odsłon i działa z Next.js, Gatsby, Nuxt, Astro, SvelteKit, Remix i zwykłym HTML. Integracja jest taka sama we wszystkich przypadkach: jeden tag skryptu, poprawnie umieszczony.