Zmienne GTM to nazwane miejsca, które Google Tag Manager wypełnia wartością w chwili, gdy dzieje się zdarzenie: adres strony, kliknięty link, cena produktu z warstwy danych, plik cookie, stała w rodzaju identyfikatora pomiaru GA4. Zmienne wbudowane są gotowe i wystarczy je włączyć. Zmienne zdefiniowane przez użytkownika tworzysz sam, a najczęściej będziesz używać zmiennej warstwy danych (Data Layer Variable).

Tagi decydują, co wysłać. Reguły decydują, kiedy. Zmienne decydują, co faktycznie jest w środku.

Dlatego zepsuta zmienna rzadko psuje coś, co widać gołym okiem. Tag dalej się uruchamia. Reguła dalej pasuje. A GA4 spokojnie zapisuje undefined albo, co gorsze, wartość, która została po wcześniejszym kroku.

Najważniejsze wnioski
  • Zmienne wbudowane (Page URL, Click URL, Video Percent) włączasz w Zmienne > Konfiguruj. Własne tworzysz w Zmienne > Nowa.
  • Zmienne nie są liczone raz na stronę. GTM oblicza je przy każdym zdarzeniu, które ich potrzebuje, więc ta sama zmienna może mieć inną wartość na gtm.js, a inną przy kliknięciu.
  • Zmienna warstwy danych czyta scalony model danych GTM, a nie tylko ostatni push. Brakujący klucz zwraca poprzednią wartość, jeśli taka istnieje. To źródło połowy błędów w przychodach, które znajduję.
  • Tabele przeglądowe i tabele RegEx zastępują stosy niemal identycznych tagów. Wartość domyślną ustawiaj zawsze świadomie.
  • Nazywaj zmienne według typu i przeznaczenia (DLV - ecommerce.value, CONST - GA4 ID). W kontenerze ze 140 zmiennymi nazwy są Twoją jedyną dokumentacją.

Wbudowane a zdefiniowane przez użytkownika: dwie połowy

Zmienne wbudowane przygotował Google. Nie konfigurujesz ich, tylko włączasz w Zmienne > Zmienne wbudowane > Konfiguruj. Świeży kontener internetowy ma włączonych pięć: Page URL, Page Hostname, Page Path, Referrer i Event. Reszta siedzi w grupach, które włączasz w razie potrzeby:

  • Strony: Page URL, Page Hostname, Page Path, Referrer.
  • Narzędzia: Event, Container ID, Container Version, Environment Name, Random Number, HTML ID, a do tego nowsze Analytics Client ID i Analytics Session ID.
  • Kliknięcia: Click Element, Click Classes, Click ID, Click Target, Click URL, Click Text.
  • Formularze, Historia, Błędy, Przewijanie, Widoczność i Filmy (Video Percent, Video Title, Video Status i pięć kolejnych).

Większość zmiennych wbudowanych po prostu czyta klucz gtm.*, który wypchnęła reguła. Click URL czyta gtm.elementUrl. Video Percent czyta gtm.videoPercent.

Nie ma reguły kliknięcia, nie ma Click URL. Dlatego Click URL użyty w tagu odsłony strony jest zawsze pusty.

Zmienne zdefiniowane przez użytkownika znajdziesz w Zmienne > Zmienne zdefiniowane przez użytkownika > Nowa > Konfiguracja zmiennej. Tu dzieje się prawdziwa robota. Czytasz warstwę danych, mapujesz wartości, rozbierasz adresy URL, piszesz małe funkcje. Google dzieli je na pięć grup: Nawigacja, Zmienne strony, Elementy strony, Narzędzia, Dane kontenera.

Typy zmiennych GTM w pigułce

Oto ściąga, którą mam otwartą podczas audytów. Nie każdy typ, ale każdy, który widuję w prawdziwych kontenerach.

Typ zmiennej Co czyta Typowe użycie Na co uważać
Zmienna warstwy danych Klucz w modelu danych GTM ecommerce.value, transaction_id, user_type Stare wartości z wcześniejszych pushy, zła wersja.
Stała Stały tekst Identyfikator pomiaru GA4, identyfikator konwersji Ads Wpisywanie tego samego ID ręcznie w 12 tagach.
Tabela przeglądowa Dokładne dopasowanie innej zmiennej Host na identyfikator pomiaru, ścieżka na typ strony Brak wartości domyślnej, więc niedopasowane wejście daje undefined.
Tabela RegEx Dopasowanie wzorca, od góry do dołu Grupowanie adresów w sekcje Wygrywa pierwsze dopasowanie, kolejność ma znaczenie.
URL Część adresu URL Parametr zapytania, host z Click URL Źródło domyślnie ustawione na Page URL.
Plik cookie własny document.cookie po nazwie Cookie zgód, grupa testu A/B Nie odczyta ciasteczek HttpOnly.
Element DOM Tekst lub atrybut elementu Nazwa produktu w h1, atrybuty data- Elementu jeszcze nie ma w DOM przy Container Load.
Zmienna JavaScript Globalna zmienna JS navigator.language, document.title Zmiana nazwy zmiennej globalnej w nowej wersji strony.
Własny kod JavaScript Wynik funkcji Drobne przekształcenia, wyszukiwanie przez closest() Efekty uboczne, wiele uruchomień na stronę.
Tag Google: ustawienia konfiguracji Parametry konfiguracji Wspólne ustawienia wszystkich tagów Google Zmiana zmienia każdy tag, który jej używa.
Tag Google: ustawienia zdarzenia Parametry zdarzenia page_type, logged_in w każdym zdarzeniu GA4 Parametry, o których dodawaniu zapomniałeś.

Zmienna warstwy danych i dlaczego jej wersja ma znaczenie

Zmiennych warstwy danych utworzysz więcej niż jakichkolwiek innych. Zmienne > Nowa > Zmienna warstwy danych, wpisujesz nazwę klucza, gotowe. Poza jednym polem, którego większość ludzi nigdy nie rusza: Wersja warstwy danych.

Wersja 2 (domyślna) traktuje kropki jako zagnieżdżenie. ecommerce.value oznacza "klucz value wewnątrz obiektu ecommerce". Sięgniesz też do tablic: ecommerce.items.0.item_id daje ID pierwszego produktu.

Wersja 1 traktuje kropki jako część nazwy klucza. Push {"a.b.c": "value"} zostaje jednym kluczem o nazwie dosłownie a.b.c. Przez ostatnie pięć lat widziałem dokładnie jeden sensowny powód, żeby jej użyć: starą stronę, której developerzy wypychali klucze z kropkami w nazwach. Jeśli w kontenerze masz zmienne w wersji 1, których nie wybrałeś świadomie, prawdopodobnie nic nie czytają.

Zachowanie, które gryzie ludzi, to sposób, w jaki wersja 2 pamięta. GTM nie czyta "ostatniego pusha". Trzyma scalony model danych: każdy push jest dołączany do tego, co już tam jest. Jeśli klucza nie ma w bieżącym pushu, zmienna zwraca to, co zostawił któryś z wcześniejszych.

Damian prowadzi sklep internetowy z suplementami, około 2600 zamówień miesięcznie. Jego zdarzenie purchase korzystało z DLV - ecommerce.value.

Po przebudowie checkoutu developerzy przenieśli sumę zamówienia do nowego klucza, ecommerce.order_total, i przestali wysyłać value. Nic nie zgłosiło błędu. Zmienna dalej zwracała wartość z ostatniego pusha add_to_cart na stronie, czyli cenę jednego produktu, gdzieś między 60 a 90 zł.

Przez sześć tygodni przychód w GA4 był o mniej więcej 41 000 zł niższy niż w panelu sklepu. Tag strzelał przy każdym zamówieniu. DebugView pokazywał value przy każdym zakupie. Po prostu nie tę liczbę.

Dwie poprawki, obie tanie:

  1. Wypychaj { ecommerce: null } przed każdym zdarzeniem ecommerce, tak jak w przykładach Google dla GA4. To czyści stary obiekt z modelu danych.
  2. Co tydzień porównuj przychód w GA4 z backendem. To jedyny test, który łapie wiarygodnie wyglądającą, błędną liczbę.

Poradnik o warstwie danych GA4 i GTM opisuje szczegółowo stronę pushy.

Tabele przeglądowe i RegEx: mniej tagów, jedna decyzja

Tabela przeglądowa bierze zmienną wejściową i mapuje dokładne wartości na wyniki. Klasyka: ruch ze stagingu kierowany do testowej usługi.

Gdy {{Page Hostname}} równa się Ustaw {{LT - GA4 measurement ID}} na
sklep.example.com G-PROD12345
staging.example.com G-TEST67890
(wartość domyślna) G-TEST67890

Ten ostatni wiersz to cały sens.

Sklep meblowy Justyny w Łodzi miał dokładnie taką tabelę, tylko wartość domyślną ustawiono na produkcyjne ID, "żeby nic nie zginęło".

Potem agencja developerska postawiła preview-482.example.com pod redesign i puściła na nim automatyczne testy checkoutu. W marcu do produkcyjnego GA4 wpadło 3400 dodatkowych sesji i 212 testowych zakupów. Tabela zrobiła dokładnie to, co jej kazano.

Powiem wprost: wartość domyślna tabeli przeglądowej ma być wartością bezpieczną, nigdy produkcyjną. Nieznane wejście znaczy, że nie wiesz, skąd przychodzi ruch. Traktuj go jak dane testowe.

Tabela RegEx robi to samo z wyrażeniami regularnymi. Wzorce są sprawdzane od góry do dołu i wygrywa pierwsze dopasowanie. Domyślnie wzorzec musi pasować do całego wejścia i ignoruje wielkość liter.

W Ustawieniach zaawansowanych możesz wyłączyć Tylko pełne dopasowania. Możesz też włączyć Grupy przechwytywania i zamiana i używać $1 w wyniku.

Dokumentacja Google ostrzega, że grupy przechwytywania przy wyłączonym pełnym dopasowaniu potrafią zwracać dziwne wyniki. Ma rację. Zostawiam pełne dopasowanie włączone i dopisuję .* tam, gdzie trzeba.

Custom JavaScript, DOM, cookie i cała reszta

Własny kod JavaScript (Custom JavaScript) musi być anonimową funkcją, która zwraca wartość:

function() {
          var path = {{Page Path}} || '';
          if (path.indexOf('/blog/') === 0) return 'blog';
          if (path.indexOf('/produkt/') === 0) return 'product';
          return 'other';
        }
        

Dwie zasady. Po pierwsze, zero efektów ubocznych: żadnego dataLayer.push(), żadnych zmian w DOM, żadnego zapisywania cookies. GTM może obliczyć tę samą zmienną wiele razy na jednej stronie, raz przy każdym zdarzeniu, które jej używa, więc push w środku zmiennej robi pętlę dodatkowych zdarzeń.

Po drugie, krótko. Każda linijka wlicza się w limit rozmiaru kontenera, czyli 200 KB, a 40-linijkowa funkcja do typu strony to tabela RegEx w przebraniu.

Element DOM przyjmuje metodę wyboru (ID albo selektor CSS) i opcjonalną nazwę atrybutu. Bez atrybutu zwraca tekst elementu. DOM czyta w chwili zdarzenia, więc przy Container Load (gtm.js) elementu może jeszcze nie być. Używaj go od DOM Ready w górę.

Plik cookie własny (1st-Party Cookie) czyta ciasteczko po nazwie, z opcją dekodowania URI. Jeśli dwa ciasteczka mają tę samą nazwę, zwraca pierwsze. Nie widzi ciasteczek HttpOnly, bo nie widzi ich żaden JavaScript.

Zmienna JavaScript czyta zmienną globalną po ścieżce, np. navigator.language. To nie kod, tylko ścieżka. W porządku dla wartości, które platforma wystawia na stronie. Krucha, gdy aktualizacja motywu zmieni nazwę zmiennej globalnej.

Stała to najbardziej niedoceniany typ. Jedna CONST - GA4 measurement ID używana wszędzie oznacza jedno miejsce do zmiany przy migracji usługi.

Tag Google: ustawienia konfiguracji i Tag Google: ustawienia zdarzenia trzymają grupy parametrów do wielokrotnego użytku. Pierwsza trafia do tagów Google, druga do tagów zdarzeń GA4. Świetnie sprawdzają się dla page_type czy logged_in w każdym zdarzeniu. Tylko je dokumentuj, bo parametr dodany po cichu przez zmienną ustawień jest niewidoczny, kiedy otwierasz pojedynczy tag.

Kiedy zmienne są obliczane (i co naprawdę znaczy undefined)

Tego prawie nikt nie tłumaczy. GTM nie oblicza zmiennych przy ładowaniu strony i nie trzyma ich w zapasie. Oblicza zmienną wtedy, gdy coś jej potrzebuje: reguła sprawdza warunek albo uruchamiany tag czyta pole. Każde zdarzenie w podglądzie (Container Loaded, DOM Ready, Link Click, Twoje własne zdarzenia) ma własną zakładkę Zmienne z wartościami z tej chwili.

Czyli {{Click Text}} jest pusty na Container Loaded i wypełniony na Link Click. {{DLV - user_type}} może mieć undefined na gtm.js, a "logged_in" trzy zdarzenia później, gdy strona go wypchnie. Jeśli Twój tag strzela na Wszystkich stronach, czyta tę wczesną wartość.

undefined zwykle znaczy jedną z trzech rzeczy:

  • Klucz nigdy nie został wypchnięty albo został wypchnięty po zdarzeniu, na którym strzela tag.
  • Nazwa klucza się nie zgadza. transactionId i transaction_id to różne klucze. Wielkość liter ma znaczenie.
  • Zmienna czyta złe źródło, np. zmienna URL dalej wskazuje na Page URL zamiast Click URL.

Pomagają dwa ustawienia. Ustaw wartość domyślną w zmiennych warstwy danych i w tabelach daje wartość zapasową. Formatuj wartość (w ustawieniach każdej zmiennej) zmienia wielkość liter i podmienia undefined, null, true albo false na Twój tekst. Używam "Konwertuj undefined na" not_set przy parametrach, które chcę widzieć w raportach jako dziury, a nie jako ciche braki.

Konwencja nazw, która przetrwa 140 zmiennych

Kontenery, które audytuję, mają od 60 do 150 zmiennych zdefiniowanych przez użytkownika. Bez konwencji dostajesz price, Price 2, dl price NEW i price (nie używać). Wszystkie cztery aktywne.

Mój format: TYP - co przechowuje.

  • DLV - ecommerce.value (ścieżka klucza, dokładnie).
  • CONST - GA4 measurement ID.
  • LT - hostname to GA4 ID.
  • RT - page path to page type.
  • URL - Click Hostname, URL - query utm_campaign.
  • CJS - link location.
  • COOKIE - cmp consent.
  • DOM - product h1.
  • GT EVENT - common params.

Sortuje się według typu. Mówi, skąd pochodzi wartość. A junior przeczyta tag bez otwierania sześciu zmiennych.

Nieużywane zmienne wylatują. Moja lista kontrolna audytu GTM ma kroki sprzątania. Stare zmienne zasilające żywe tagi to jeden z 5 błędów GTM, które po cichu kosztują konwersje.

Chcesz zobaczyć to w prawdziwym wdrożeniu? Tekst o śledzeniu kliknięć w linki w GA4 przez GTM krok po kroku używa Click URL, zmiennej URL dla klikniętego hosta i małej zmiennej Custom JavaScript.

Najczęstsze pytania

Czym się różnią zmienne wbudowane od zdefiniowanych przez użytkownika w GTM? Wbudowane pochodzą od Google. Tylko je włączasz w Zmienne > Konfiguruj. Własne budujesz sam, np. zmienną warstwy danych dla transaction_id albo stałą z identyfikatorem pomiaru.

Dlaczego moja zmienna warstwy danych zwraca undefined? Klucz nigdy nie został wypchnięty, został wypchnięty po zdarzeniu, na którym strzela tag, albo jego nazwa nie zgadza się co do znaku, łącznie z wielkością liter. Otwórz podgląd, wybierz zdarzenie, na którym strzela tag, i sprawdź w zakładce Warstwa danych, czy klucz jest tam w tej chwili.

Wersja 1 czy 2 warstwy danych? Wersja 2, domyślna, praktycznie zawsze. Czyta kropki jako zagnieżdżenie, więc ecommerce.value działa. Wersja 1 traktuje kropki jako zwykłe znaki w nazwie klucza i pasuje tylko do starych wdrożeń, które wypychają klucze w stylu "a.b.c".

Kiedy tabela przeglądowa, a kiedy tabela RegEx? Tabela przeglądowa, gdy wejścia to dokładne, znane wartości, np. hosty. Tabela RegEx, gdy dopasowujesz wzorce, np. grupujesz tysiące adresów w typy stron. Tabela RegEx zatrzymuje się na pierwszym dopasowaniu, więc tam liczy się kolejność. Obie potrzebują świadomie ustawionej wartości domyślnej.

Czy zmienna Custom JavaScript może wypychać dane do warstwy danych? Może, ale nie powinna. GTM może ją uruchomić przy każdym zdarzeniu, które jej używa, więc push w środku tworzy dodatkowe zdarzenia. Trzymaj zmienne Custom JavaScript w czystości: odczyt, przekształcenie, zwrot.

Ile zmiennych może mieć kontener GTM? Nie ma sztywnego limitu liczby, pod który warto planować. Prawdziwą granicą jest rozmiar kontenera, 200 KB, i Twoja własna zdolność do zrozumienia całości. Przy każdym audycie usuwaj nieużywane.

Co dalej

Otwórz w kontenerze ekran Zmienne i posortuj listę zmiennych użytkownika po nazwie. Jeśli o połowie z nich nie umiesz powiedzieć, co trzymają, bez otwierania każdej, to Twoje pierwsze zadanie: zmień nazwy na TYP - przeznaczenie i usuń to, do czego nic się nie odwołuje.

Potem weź trzy najważniejsze tagi (zakup, lead, rejestracja) i sprawdź w podglądzie każdą zmienną, której używają, na dokładnie tym zdarzeniu, na którym strzelają. Nie na Container Loaded. Na prawdziwym zdarzeniu. Tam chowają się undefined i stare wartości.

Nie chcesz robić tego ręcznie? Darmowy audyt GTM wskaże zepsute i nieużywane zmienne, które widzi w Twoim kontenerze. Przy kontenerach, które zmieniają się co sprint, abonament na monitoring GTM pilnuje ich na bieżąco, żeby przemianowany klucz w warstwie danych nie kosztował Cię sześciu tygodni danych o przychodach.


Źródła i dalsza lektura - User-defined variable types for web (Pomoc Menedżera tagów) - Built-in variables for web containers (Pomoc Menedżera tagów) - The data layer (Google for Developers)

Ile Twoich zmiennych GTM zwraca teraz undefined?

Darmowy audyt GTM wypisuje nieużywane, zepsute i zdublowane zmienne w kontenerze i wskazuje te, które wysyłają błędne wartości do GA4 i tagów reklamowych.

Zacznij darmowy audyt GTM
Piotr Litwa

Piotr Litwa

GTM & Analytics Specialist

Piotr monitoruje kontenery GTM i usługi GA4 dla firm w całej Europie, wyłapując problemy z trackingiem, zanim zaczną kosztować konwersje. 120+ klientów, 10+ lat, zero outsourcingu.