Google Tag Manager Setup on 1C-Bitrix
GTM is a layer between your site and analytics. Instead of asking developers to add pixels and tracking codes each time, marketers configure triggers and tags in GTM themselves. But for this to work, developers need to correctly install GTM once and set up dataLayer with e-commerce events.
Installing GTM into Bitrix Template
GTM requires two code snippets: one in <head>, another right after <body>. In Bitrix template (/bitrix/templates/[name]/header.php):
<?php
// GTM container ID from Google Tag Manager account
$gtmId = \Bitrix\Main\Config\Option::get('custom', 'gtm_container_id', 'GTM-XXXXXXX');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- GTM in head (as early as possible) -->
<?php if ($gtmId): ?>
<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','<?= htmlspecialchars($gtmId) ?>');</script>
<?php endif; ?>
</head>
<body>
<!-- GTM noscript right after <body> -->
<?php if ($gtmId): ?>
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<?= htmlspecialchars($gtmId) ?>"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<?php endif; ?>
Store container ID in b_option (settings table) via admin interface — not in code, so marketers can change it without deployment.
dataLayer for Product Pages
For extended e-commerce (GA4 Ecommerce) you need dataLayer with product data. In bitrix:catalog.element component template or in result_modifier.php:
// bitrix/templates/.default/components/bitrix/catalog.element/.default/result_modifier.php
if (!empty($arResult['CATALOG_PRICE']['BASE']['PRICE'])) {
$product = [
'item_id' => $arResult['ID'],
'item_name' => $arResult['NAME'],
'price' => (float)$arResult['CATALOG_PRICE']['BASE']['PRICE'],
'currency' => $arResult['CATALOG_PRICE']['BASE']['CURRENCY'],
'item_category' => $arResult['SECTION']['NAME'] ?? '',
'item_brand' => $arResult['PROPERTIES']['BRAND']['VALUE'] ?? ''
];
$this->arResult['GTM_PRODUCT'] = $product;
}
In component template output dataLayer before closing </body>:
<?php if (!empty($arResult['GTM_PRODUCT'])): ?>
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'view_item',
ecommerce: {
currency: '<?= htmlspecialchars($arResult['GTM_PRODUCT']['currency']) ?>',
value: <?= (float)$arResult['GTM_PRODUCT']['price'] ?>,
items: [<?= json_encode($arResult['GTM_PRODUCT'], JSON_UNESCAPED_UNICODE) ?>]
}
});
</script>
<?php endif; ?>
Purchase Event After Payment
The most important event — actual purchase. In bitrix:sale.order.ajax component or on "Thank You" page:
<?php
// Get user's last order
$orderId = (int)($_SESSION['SALE_ORDER_ID_REDIRECTED'] ?? 0);
if ($orderId > 0) {
$order = \Bitrix\Sale\Order::load($orderId);
if ($order) {
$items = [];
foreach ($order->getBasket() as $basketItem) {
$items[] = [
'item_id' => $basketItem->getProductId(),
'item_name' => $basketItem->getField('NAME'),
'price' => (float)$basketItem->getPrice(),
'quantity' => (int)$basketItem->getQuantity()
];
}
?>
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '<?= $order->getId() ?>',
value: <?= (float)$order->getPrice() ?>,
currency: '<?= $order->getCurrency() ?>',
items: <?= json_encode($items, JSON_UNESCAPED_UNICODE) ?>
}
});
</script>
<?php
}
}
?>
Add to Cart via JS
The add_to_cart event is generated in JavaScript when clicking "Add to Cart". Intercept Bitrix standard event:
// In template or in /local/templates/.default/js/analytics.js
BX.addCustomEvent('OnBasketChange', function(event) {
if (event.action === 'ADD') {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'add_to_cart',
ecommerce: {
currency: event.currency || 'RUB',
value: event.price * event.quantity,
items: [{
item_id: String(event.productId),
item_name: event.productName || '',
price: event.price,
quantity: event.quantity
}]
}
});
}
});
The OnBasketChange event is generated by sale module via BX.Event when AJAX-adding to cart.
Verifying It Works
Install Google's "Tag Assistant" extension in browser. It shows which GTM tags fired, with what data, and any dataLayer errors.
Or from browser console:
// View all events in dataLayer
console.table(window.dataLayer);
// Watch for new events
const origPush = window.dataLayer.push.bind(window.dataLayer);
window.dataLayer.push = function(data) {
console.log('dataLayer push:', data);
return origPush(data);
};
Storing Container ID
Don't hardcode GTM ID in template — use b_option:
// Write via API
\Bitrix\Main\Config\Option::set('custom', 'gtm_container_id', 'GTM-XXXXXXX');
// Read
$gtmId = \Bitrix\Main\Config\Option::get('custom', 'gtm_container_id');
This allows changing ID without code edits via admin interface or custom module settings.







