İçeriğe geç
ceaksan

dataLayer Nasıl Çalışır? GTM, gtag.js ve Consent Mode'un Ortak Katmanı

dataLayer yalnızca Google Tag Manager'a özel değil; gtag.js ve Consent Mode V2 de aynı nesneyi kullanır. Initialization order hatalarının neden veri kaybına yol açtığını ve push() ile array tanımı arasındaki farkı ele alıyoruz.

2 Nis 2019 3 dk okuma Güncellendi: 8 Nis 2026
TL;DR

window.dataLayer, Google Tag Manager'ın, gtag.js'in ve Consent Mode V2'nin aynı anda kullandığı bir JavaScript FIFO kuyruğudur. En sık görülen sorun initialization order hatasıdır: dataLayer GTM container'dan sonra tanımlanırsa, sayfa yüklenirken push edilen veriler kaybolur. gtag() ise yalnızca dataLayer.push(arguments) wrapper'ıdır; özel GTM komutları (set, reset) doğrudan push() ile çalışır, gtag() üzerinden değil.

Ç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 ManagerContainer yüklenirken dataLayer’ı okur; trigger, variable ve event olarak işler
gtag.jsgtag() fonksiyonu dataLayer.push(arguments) wrapper’ıdır
Consent Mode V2gtag('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" });

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.

not

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

Footnotes

  1. Google Tag (gtag.js). Google Developers
  2. Google Tag Manager’s Data Model. Simo Ahava
  3. Use gtag.js Parameters In Google Tag Manager. Simo Ahava
  4. Data layer. Tag Manager Help. Google 2
  5. Consent Mode. Google Tag Platform Developer Guide
  6. Developer Guide. Google Tag Manager. Google Developers
Önemli Noktalar
  • 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
Sık Sorulan Sorular (FAQ)
+ 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.