Analytics Architecture on Blogger
Blogger uses an XML-based template system where the entire site layout is defined in a single XML template. Analytics scripts can be injected through several mechanisms:
- XML template editing -- direct modification of the Blogger template XML to add
<script>tags in the<head>or<body>sections - HTML/JavaScript gadgets -- widgets added through the Layout editor that render arbitrary HTML and JavaScript
<b:section>and<b:widget>-- Blogger's template tags that define content regions and widget containers- Template data variables --
<data:blog.url/>,<data:post.title/>, and other server-rendered variables available in the template - Built-in Google Analytics integration -- native field for a GA tracking ID in Blogger settings
Blogger's hosting is fully managed by Google. There is no server-side access, no file system, and no ability to install plugins. All customization happens through the template XML or gadget widgets.
Installing Tracking Scripts
Built-In Google Analytics Field
Blogger has a native GA integration under Settings > Other > Google Analytics:
- Enter a measurement ID (
G-XXXXXXXXXX) or a legacyUA-tracking ID - Blogger injects the GA snippet automatically on every page
- This method only supports basic pageview tracking; no events or e-commerce
XML Template Script Injection
For GTM or any custom analytics, edit the template directly. Navigate to Theme > Edit HTML:
<?xml version="1.0" encoding="UTF-8" ?>
<html>
<head>
<!-- Blogger system tags -->
<b:include data='blog' name='all-head-content'/>
<!-- Google Tag Manager - add after all-head-content -->
<script>
//<![CDATA[
(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-XXXXXX');
//]]>
</script>
</head>
Note the <![CDATA[ ... ]]> wrapper. Blogger templates are XML, so JavaScript must be wrapped in CDATA blocks to prevent XML parsing errors.
HTML/JavaScript Gadget
Add a gadget through Layout > Add a Gadget > HTML/JavaScript:
<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-XXXXXX');
</script>
Gadgets render in the <body> within a <b:section> container. Placement depends on where the gadget is positioned in the Layout editor. For scripts that must be in <head>, use the XML template method instead.
Data Layer Implementation
Blogger Template Data Variables
Blogger exposes server-rendered data through template variables. These are available inside the XML template and render as plain text in the HTML output:
| Variable | Description |
|---|---|
<data:blog.url/> |
Current page URL |
<data:blog.title/> |
Blog title |
<data:blog.homepageUrl/> |
Blog home URL |
<data:blog.pageType/> |
Page type: index, item, static_page, archive, error_page |
<data:post.title/> |
Current post title (on post pages) |
<data:post.url/> |
Current post URL |
<data:post.id/> |
Post ID |
<data:post.labels/> |
Post labels/tags (array) |
<data:post.author/> |
Post author name |
<data:post.timestampISO8601/> |
Post publish date in ISO format |
Building a Data Layer in the Template
Add this inside the <b:includable id='main'> block or just before </body> in the template:
<script>
//<![CDATA[
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'page_type': '<data:blog.pageType/>',
'blog_title': '<data:blog.title/>',
'page_url': '<data:blog.url/>'
});
//]]>
</script>
Post-Specific Data Layer
Use Blogger's conditional tags to push post data only on article pages:
<b:if cond='data:blog.pageType == "item"'>
<script>
//<![CDATA[
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'view_content',
'content_type': 'blog_post',
'content_id': '<data:post.id/>',
'content_title': '<data:post.title/>',
'content_author': '<data:post.author/>',
'content_published': '<data:post.timestampISO8601/>'
});
//]]>
</script>
</b:if>
Label-Based Categorization
Blogger uses "labels" instead of categories. Access them in the template:
<b:if cond='data:post.labels'>
<script>
//<![CDATA[
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'content_labels': [
<b:loop values='data:post.labels' var='label'>
'<data:label.name/>'<b:if cond='!data:label.isLast'>,</b:if>
</b:loop>
]
});
//]]>
</script>
</b:if>
Common Issues
XML parsing errors from unescaped JavaScript -- All JavaScript in Blogger templates must be wrapped in <![CDATA[ ... ]]> blocks. Angle brackets (<, >) in JavaScript comparisons will break XML parsing. Use < and > or restructure the logic to avoid them.
Gadget script loading order -- HTML/JavaScript gadgets render in DOM order based on their position in the Layout editor. If your tracking script depends on the data layer being populated first, ensure the data layer gadget appears above the tracking script gadget.
Duplicate pageviews from built-in GA -- If you use both the native Google Analytics field and a GTM container with GA configured, you will get duplicate pageviews. Disable the built-in GA when using GTM.
Template data variables empty on non-post pages -- Variables like <data:post.title/> and <data:post.id/> are only populated on individual post pages (pageType == "item"). On the homepage, archive, and static pages, these render as empty strings. Always use conditional <b:if> blocks.
AdSense interaction tracking -- Blogger integrates natively with Google AdSense, but there is no direct way to track ad click events in the data layer. AdSense operates in a cross-origin iframe, so JavaScript event listeners on the host page cannot detect clicks inside the ad frame.
Mobile theme vs desktop theme -- Older Blogger templates had a separate mobile theme. The mobile version may not include your custom template modifications. In Theme settings, ensure "Mobile" is set to "Desktop" or "Custom" to use a single template across devices.
Platform-Specific Considerations
No server-side processing -- Blogger is a fully managed platform with no server access. All analytics implementation is client-side only. For any server-side tracking needs (conversion APIs, webhook processing), you must use an external server that receives data from client-side events.
Single template file -- The entire Blogger site layout is defined in one XML template. There are no partial templates or includes (beyond <b:includable> blocks within the same file). This makes large-scale template edits complex but also means a single point of modification for analytics.
Limited page types -- Blogger has a small set of page types: index (homepage), item (post), static_page, archive, and error_page. Your data layer logic will branch on <data:blog.pageType/> to handle each type appropriately.
Custom domain tracking -- Blogger supports custom domains (e.g., blog.example.com instead of example.blogspot.com). When switching domains, update your analytics property settings to reflect the new domain. Cross-domain tracking may be needed if the blog is a subdomain of your main site.
No e-commerce capability -- Blogger has no built-in e-commerce features, shopping cart, or product catalog. If you sell through third-party widgets or affiliate links on a Blogger site, tracking those interactions requires custom JavaScript event listeners on the relevant DOM elements.
Content Security Policy -- Blogger's hosted pages have a permissive CSP, allowing most third-party analytics scripts. However, inline script execution relies on the CDATA wrapping pattern. If Google tightens CSP in the future, gadget-based script injection may be affected.