Analytics
Google Tag Manager Setup for South African Sites.
Google Tag Manager gives South African marketers a single place to manage GA4, Meta Pixel, TikTok, and Google Ads tags without pestering a developer every sprint. This guide covers the GTM install, POPIA-aligned Consent Mode, a data layer that works on Shopify and custom stacks, and the triggers you need before any tag fires. Written for SA teams running Payfast or Peach checkouts.
Prerequisites
- A Google Tag Manager account and container created at tagmanager.google.com
- Container ID in the format GTM-XXXXXXX
- Edit access to your theme (Shopify theme.liquid or a base HTML template)
- GA4 Measurement ID ready so you can queue the first tag
- A POPIA consent banner that emits accept / reject events on the document
Step 1. Install the GTM container snippet
Paste the two GTM snippets into your theme — the script goes as high in <head> as possible, the noscript immediately after <body> opens. Replace GTM-XXXXXXX with your real container ID. Shopify merchants should use theme.liquid rather than Admin > Preferences to keep POPIA ordering under their control.
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager --> Step 2. Seed the data layer before GTM loads
Push POPIA consent defaults and currency (ZAR) before the GTM snippet so Consent Mode v2 initialises correctly. Order matters: consent defaults must run first, GTM second, everything else queues behind GTM.
<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',
region: ['ZA'],
wait_for_update: 500
});
dataLayer.push({ country: 'ZA', currency: 'ZAR' });
</script> Step 3. Add a GA4 Configuration tag inside GTM
In GTM > Tags > New, choose "Google Tag". Set the Tag ID to your GA4 Measurement ID (G-XXXXXXXXXX). Under Configuration parameters, add currency=ZAR and country=ZA. Trigger: All Pages. This replaces the theme-level gtag.js install so you get a single source of truth.
Step 4. Connect the POPIA banner to Consent Mode update
When the shopper accepts or rejects categories, dispatch a consent update event. GTM built-in Consent Mode listens to gtag('consent', 'update', ...) and unblocks tags that declared consent requirements.
document.addEventListener('popia:consent:changed', (e) => {
const c = e.detail.categories;
gtag('consent', 'update', {
analytics_storage: c.analytics ? 'granted' : 'denied',
ad_storage: c.marketing ? 'granted' : 'denied',
ad_user_data: c.marketing ? 'granted' : 'denied',
ad_personalization: c.marketing ? 'granted' : 'denied'
});
window.dataLayer.push({ event: 'consent_updated' });
}); Step 5. Push ecommerce events into the data layer
On Shopify, use Custom Pixels to bridge Shopify Web Pixels into dataLayer.push so GTM tags can pick up purchase, add_to_cart, and begin_checkout. Keep the event names aligned with the GA4 recommended schema so downstream tags (Meta, TikTok) can reuse them.
analytics.subscribe('checkout_completed', (event) => {
const c = event.data.checkout;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: c.order.id,
value: Number(c.totalPrice.amount),
currency: c.totalPrice.currencyCode,
tax: Number(c.totalTax?.amount || 0),
shipping: Number(c.shippingLine?.price.amount || 0),
items: c.lineItems.map((li) => ({
item_id: li.variant.sku,
item_name: li.title,
price: Number(li.variant.price.amount),
quantity: li.quantity
}))
}
});
}); Step 6. Configure built-in variables and a purchase trigger
Enable built-in variables Page Path, Click URL, and Form ID. Create a Custom Event trigger named "Purchase" that fires when Event equals "purchase". Reuse this trigger for GA4 purchase, Meta Pixel Purchase, TikTok CompletePayment, and Google Ads conversion.
Step 7. Preview with Tag Assistant and GA4 DebugView
Click Preview in GTM, open your store in the debug window, and walk through add-to-cart through thank-you. Verify every tag has "Consent: Met" before it fires. If you see tags firing under "Consent: Not required" when they read cookies, the tag is misconfigured — edit the tag and set Consent Settings > Require additional consent.
Step 8. Publish and version with a named release
Name every GTM version (e.g. "2026-04-15: GA4 purchase + Meta CAPI"). Versioning saves you when a freelancer pushes a broken tag at 17:00 on a Friday. Turn on 2FA for every GTM account user, and remove ex-agency accounts the week the contract ends.
SA gotchas
- GTM loaded before consent defaults will fire GA4 page_view immediately, violating POPIA. The consent default script must be above the GTM snippet in the DOM.
- Shopify Customer Events run in a sandbox with no document access, so window.dataLayer.push works but direct DOM manipulation does not — do not try to read cookies from inside the pixel.
- Server-side GTM on Cloudflare Workers is supported but needs a custom domain (e.g. sgtm.yourstore.co.za) to avoid Safari ITP capping cookies at 7 days.
- Tags set to "Consent not required" will fire regardless of POPIA banner state. Audit every tag quarterly because vendor templates default to this.
- GTM container size has a 200 tag soft cap. SA stores that bolt on every affiliate tracker hit this within a year — prune dead tags before adding new ones.
Frequently asked questions
Do I need Google Tag Manager if I only run GA4 and Meta Pixel?
Not strictly, but GTM centralises POPIA consent logic, makes it easier to add a third or fourth tag later, and versions every change. For two tags and no plan to grow, direct installs are fine.
Can I use GTM for server-side tagging in South Africa?
Yes. Server-side GTM runs on Google Cloud Run (use the europe-west1 region for lowest latency to SA) or on a Cloudflare Worker. It lets you enrich events with CAPI and deduplicate against client events.
Will GTM slow down my Shopify store?
The container script is about 70KB gzipped and loads async. The real weight comes from what you put inside it. Audit tags with the Tag Assistant Performance tab and ditch anything under 0.5 percent utility.
How do I keep GTM POPIA compliant?
Consent defaults denied, tags configured with "Require additional consent", document your tag inventory in your PAIA manual, and sign the Google Data Processing Addendum.