Çoğu anlatıda dataLayer yalnızca Google Tag Manager’a özgü bir yapı gibi sunulur. Ama window.dataLayer nesnesi aynı zamanda gtag() fonksiyonu tarafından, Consent Mode V2 tarafından da kullanılır. Bu üçünün aynı nesneyi paylaşması, initialization order’ı kritik kılar; yanlış sıralama sessizce veri kaybına yol açar.
Hangi Araçlar dataLayer Kullanır?
| Araç | dataLayer ile İlişkisi |
|---|---|
| Google Tag Manager | Container yüklenirken dataLayer’ı okur; trigger, variable ve event olarak işler |
| gtag.js | gtag() fonksiyonu dataLayer.push(arguments) wrapper’ıdır |
| Consent Mode V2 | gtag('consent', 'default', {...}) çağrısı da dataLayer’a push eder |
Google’ın kendi gtag.js implementasyonunda1 fonksiyon tam olarak şu şekilde tanımlanır:
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag() çağrılarının tamamı bu nesne üzerinden iletilir. Dolayısıyla gtag('event', 'purchase', {...}) ile dataLayer.push({event: 'purchase', ...}) aynı nesneyi kullanır; fark yalnızca Google’ın kendi tag’lerinin gtag formatını nasıl yorumladığında.
GTM’in İç Modeli ve Recursive Merge
window.dataLayer dizisi ile GTM’in içsel veri modeli (abstract data model) aynı şey değildir. GTM her push() çağrısını işlediğinde verileri kendi iç modeline kopyalar; GTM değişkenleri bu modeli okur, dataLayer dizisini doğrudan değil2.
Bu iç model biriktirici çalışır. Her push, mevcut model üzerine recursive merge ile uygulanır: primitive değerler (string, number) tamamen yenisi ile değiştirilir; nesneler ve diziler ise key-by-key merge edilir ve mevcut key’ler güncellenir, yeni push’ta olmayan key’ler korunur.
dataLayer.push({ pageCategory: "product", ecommerce: { currency: "USD" } });
// Model: { pageCategory: 'product', ecommerce: { currency: 'USD' } }
dataLayer.push({ event: "view_item", ecommerce: { item_id: "123" } });
// Model: { pageCategory: 'product', ecommerce: { currency: 'USD', item_id: '123' }, event: 'view_item' }
// currency hala var — recursive merge, overwrite değil
UA’dan GA4’e geçiş sürecinde bu davranış sorun yaratır. eventCategory, eventAction, eventLabel, transactionId gibi UA formatı anahtarlar eski GTM tag’leri tarafından push edildikten sonra modelde birikir. Yeni GA4 event’leri bu anahtarları temizlemediğinden GTM preview’ında her event’in yanında UA kalıntıları görünmeye devam eder.
Belirli bir anahtarı modelden kaldırmanın iki yolu:
// ecommerce nesnesini bir sonraki event'e sızdırma — Google'ın önerdiği yöntem
dataLayer.push({ ecommerce: null });
// UA kalıntılarını temizle
dataLayer.push({
eventCategory: undefined,
eventAction: undefined,
eventLabel: undefined,
});
eventModel anahtarı: GTM ve gtag.js aynı sayfada çalışırken gtag’ın event parametreleri dataLayer’da eventModel adlı üst seviye anahtar altında görünür3. Bu anahtar GTM tarafından değil, gtag.js runtime’ı tarafından oluşturulur; gtag’ın kendi iç parametrelerini nasıl sakladığının yansımasıdır. GTM preview konsolunda bir event’te eventModel bloğunu görüyorsanız sayfada aktif bir gtag.js implementasyonu çalışıyor demektir.
Initialization Order: Verinin Neden Kaybolduğu
GTM container yüklendiği anda dataLayer’ı okur. Eğer dataLayer GTM snippet’inden sonra tanımlanmışsa, sayfa yüklenirken push edilen veriler GTM tarafından hiç görülmez.
Hatalı sıralama:
<!-- 1. GTM yükleniyor -->
<script>
(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXX');
</script>
<!-- 2. Sonra dataLayer push — GTM bu veriyi görmedi bile -->
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
pageType: "product",
userType: "member",
});
</script>
Doğru sıralama:
<!-- 1. ÖNCE dataLayer -->
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
pageType: "product",
userType: "member",
});
</script>
<!-- 2. SONRA GTM -->
<script>
(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXX');
</script>
Veriyi GTM’den önce iletmek mümkün değilse (asenkron yükleme, lazy render), push() sonrası gtm.dom veya gtm.load tetikleyicileri ile yeniden işleme alınabilir4.
dataLayer = [] ve dataLayer.push() Farkı
Bu iki kullanım biçimi farklı sonuçlar üretir ve karıştırılması en yaygın veri kaybı nedenlerinden biridir.
// YANLIŞ: Nesneyi sıfırlar, GTM ve gtag'ın eklediği her şey gider
dataLayer = [];
// YANLIŞ: Yine sıfırlar, varsa üzerine yazar
window.dataLayer = [];
// DOĞRU: Varsa kullan, yoksa oluştur — veriyi korur
window.dataLayer = window.dataLayer || [];
dataLayer.push({ pageType: "product" });
dataLayer = [{...}] array literal sözdizimi yalnızca sayfa yüklenirken, GTM’den önce, statik sayfa bilgilerini taşımak için kullanılabilir. Ancak window.dataLayer = window.dataLayer || [] ile başlamak her durumda daha güvenlidir.
// Sayfa yüklenirken statik veri — GTM'den önce
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
pageCategory: "checkout",
visitorType: "returning",
});
// Kullanıcı etkileşimi sonrası — push() ile
dataLayer.push({ event: "add_to_cart", item_id: "12345" });
FIFO Sırası ve Consent Önceliği
dataLayer bir FIFO (First-In, First-Out / İlk Giren İlk Çıkar) kuyruğu olarak çalışır: öğeler sıraya girme sıralarıyla işlenir. Ancak consent komutları bu FIFO sırasından önce işlenir5. Bu nedenle consent default tanımı GTM container’dan önce gelmek zorundadır.
<!-- 1. Consent default — GTM'den önce zorunlu -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("consent", "default", {
ad_storage: "denied",
analytics_storage: "denied",
ad_user_data: "denied",
ad_personalization: "denied",
wait_for_update: 500,
});
</script>
<!-- 2. GTM container -->
<script>
(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXX');
</script>
Kullanıcı consent banner’da tercihini belirttiğinde, CMP gtag('consent', 'update', {...}) ile günceller; GTM bu güncellemeyi alarak beklettiği tag’leri devreye sokar. Consent mekanizmasının detayları için Consent Sınırları İçinde Reklam Ölçümü yazısına bakabilirsiniz.
GTM’e Özgü Komutlar: set ve reset
Bu iki komut yalnızca doğrudan dataLayer.push() ile çalışır; gtag() üzerinden iletilirler GTM tarafından işlenemez.
// GTM model'ine değer yaz — gtag() üzerinden ÇALIŞMAZ
dataLayer.push(["set", "pageCategory", "product-detail"]);
// GTM model'ini sıfırla
dataLayer.push(["reset"]);
set komutu, GTM’in internal model’ine doğrudan değer yazar; bu değer sonraki event’lerde variable olarak okunabilir. reset ise session boyunca biriken model verilerini temizler. Her ikisi de GTM-specific; Analytics veya Ads tag’leriyle değil, yalnızca GTM variable sistemiyle ilgilidir6.
GTM’de Değişken Tanımlama
GTM tarafında Variables > User-Defined Variables > New > Data Layer Variable adımlarıyla dataLayer’dan değer okunur. Variable ismi büyük/küçük harf dahil birebir eşleşmek zorundadır: pageCategory ile pagecategory GTM için farklı değişkendir.
Data Layer Version alanı Version 2 olarak kalmalıdır. Version 2, nested objeleri (örneğin ecommerce.items[0].item_name) doğru okuyabilir; Version 1 yalnızca düz anahtar-değer yapılarında çalışıyordu4.
Ecommerce event formatları (purchase, add_to_cart, view_item_list) bu yazının kapsamı dışında. GA4 ecommerce push yapısı için GTM ve E-Ticaret Etkinlikleri yazısına bakabilirsiniz.
İlgili Yazılar
- Google Tag Manager Kurulumu — Container kurulumu ve temel yapılandırma
- GTM dataLayer ve E-Ticaret Etkinlikleri — GA4 ecommerce push formatı ve event yapısı
- Consent Sınırları İçinde Reklam Ölçümü — Consent Mode V2 mekanizması ve CMP entegrasyonu
- Çoklu GTM Kurulumu — Birden fazla container ile dataLayer paylaşımı
Footnotes
- 01 window.dataLayer tek bir nesne: GTM, gtag.js ve Consent Mode V2 hepsi bu nesneyi paylaşır
- 02 Initialization order kritiktir: dataLayer tanımı ve consent default, GTM container'dan önce gelmek zorundadır
- 03 dataLayer = [] yazmak var olan veriyi siler; her zaman window.dataLayer = window.dataLayer || [] kullanılmalıdır
- 04 gtag() fonksiyonu doğrudan dataLayer.push(arguments) wrapper'ıdır; set/reset gibi GTM komutları gtag() üzerinden değil, doğrudan push() ile gönderilir
- 05 Consent API, FIFO kuyruğundaki diğer item'lardan önce işlenir; bu nedenle consent default GTM'den önce tanımlanmalıdır
+ dataLayer yalnızca Google Tag Manager ile mi çalışır?
Hayır. window.dataLayer nesnesi GTM, gtag.js ve Consent Mode V2 tarafından ortaklaşa kullanılır. gtag() fonksiyonu doğrudan dataLayer.push(arguments) çağrısına dönüşür. Bu üçü aynı JavaScript nesnesini paylaşır.
+ dataLayer neden GTM snippet'inden önce tanımlanmalıdır?
GTM yüklendiği anda dataLayer'ı okur. Eğer dataLayer GTM'den sonra tanımlanmışsa, sayfa yüklenirken push edilen veriler (pageType, userType gibi) kaybolur. GTM bu verileri hiç görmeden tag'leri çalıştırmaya başlar.
+ dataLayer = [] ile dataLayer.push() arasındaki fark nedir?
dataLayer = [] ifadesi nesneyi tamamen sıfırlar; GTM veya gtag'ın daha önce eklediği her şey gider. Her zaman window.dataLayer = window.dataLayer || [] kullanılmalı, ardından push() ile veri eklenmeli.
+ gtag() ile dataLayer.push() aynı şey mi?
Büyük ölçüde aynı, ancak farklar var. gtag() doğrudan dataLayer.push(arguments) wrapper'ıdır. Ancak GTM'e özgü set ve reset komutları gtag() üzerinden çalışmaz; bunlar için doğrudan dataLayer.push(['set', ...]) veya dataLayer.push(['reset']) kullanılmalıdır.
+ Consent sinyalini neden GTM'den önce göndermem gerekiyor?
GTM ilk tag'leri çalıştırmadan önce consent durumunu bilmek zorundadır. Consent default GTM'den sonra gelirse, GTM ilk tetiklenmelerde consent durumunu bilinmiyor olarak değerlendirebilir ve bazı tag'leri yanlış çalıştırabilir.
+ GTM'de Data Layer Version 1 mi Version 2 mi kullanılmalı?
Version 2 kullanılmalı. Version 2, nested objeleri (örneğin items dizisi içindeki alt objeler) doğru okuyabilir. Version 1 yalnızca düz yapılar için yeterliydi ve nested path'lerde hatalı sonuç verebilir.