Technical reference for implementing Google Analytics 4 (GA4) on Ghost, covering code injection behavior, Handlebars helper context for post/member data, membership and subscription event tracking, and Ghost's server-rendered architecture.
How GA4 Works on Ghost
Ghost is a Node.js-based CMS that server-renders pages using the Handlebars templating engine. GA4 integration relies entirely on Ghost's code injection feature -- there is no plugin system:
- Code injection (Settings > Code Injection): Ghost provides two injection points: Site Header (injected into
<head>on every page) and Site Footer (injected before</body>). The gtag.js snippet goes in Site Header. Code injection content is stored in the Ghost database and rendered server-side into every page's HTML output. - Handlebars context helpers: Ghost exposes content metadata through Handlebars block helpers that can be used inline with tracking code.
{{#post}}...{{/post}}provides access to{{author.name}},{{tags}},{{published_at}}on post pages.{{#member}}...{{/member}}exposes{{@member.status}}(free/paid/comped),{{@member.uuid}}, and{{@member.subscriptions}}for logged-in members. - No SPA behavior: Ghost renders each page as a full server response. Every navigation triggers a new page load, so
gtag('config', ...)fires naturally on each page. No virtual pageview handling is needed.
Ghost does not provide a window.dataLayer or any built-in analytics data layer. All event parameters must be constructed using Handlebars helpers (server-side) or DOM queries (client-side). For membership/subscription tracking, the {{#member}} helper is the only way to access member state -- Ghost does not expose member data to client-side JavaScript otherwise.
Ghost themes can also include tracking code in theme template files (e.g., default.hbs), but code injection is preferred because it persists across theme changes.
Installation Steps
Step 1: Get Your GA4 Measurement ID
- Sign in to Google Analytics
- Create a new GA4 property or select an existing one
- Navigate to Admin > Data Streams
- Select your web data stream
- Copy your Measurement ID (format: G-XXXXXXXXXX)
Step 2: Add GA4 to Ghost
Using Site-Wide Code Injection (Recommended)
- Log in to your Ghost Admin panel
- Navigate to Settings > Code Injection
- In the Site Header section, paste the following code:
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'anonymize_ip': true,
'cookie_flags': 'SameSite=None;Secure'
});
</script>
- Replace
G-XXXXXXXXXXwith your actual Measurement ID - Click Save
Step 3: Verify Installation
- Visit your Ghost site in a new browser window
- Open browser DevTools (F12) and check the Console
- Look for successful GA4 script loading
- Check GA4 Real-time reports to see active visitors
Configuration Options
Enhanced Tracking for Ghost
Add enhanced tracking to capture Ghost-specific data:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
// Configure with Ghost context
gtag('config', 'G-XXXXXXXXXX', {
'custom_map': {
'dimension1': 'post_author',
'dimension2': 'post_tags',
'dimension3': 'member_status'
},
'anonymize_ip': true
});
// Send Ghost context data
{{#post}}
gtag('event', 'page_view', {
'post_author': '{{author.name}}',
'post_tags': '{{tags}}',
'post_published': '{{published_at}}',
'content_type': 'post'
});
{{/post}}
{{#page}}
gtag('event', 'page_view', {
'content_type': 'page'
});
{{/page}}
</script>
Privacy and Cookie Consent
Implement GDPR-compliant tracking with consent mode:
<script>
// Set default consent state
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
'analytics_storage': 'denied',
'ad_storage': 'denied',
'wait_for_update': 500
});
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
// Update consent after user accepts
function acceptCookies() {
gtag('consent', 'update', {
'analytics_storage': 'granted'
});
}
</script>
Member Tracking
Track Ghost member activity and subscription status:
<script>
{{#member}}
// Track member status
gtag('set', 'user_properties', {
'member_status': '{{@member.status}}',
'subscription_tier': '{{@member.subscriptions}}',
'member_since': '{{@member.created_at}}'
});
// Track member as User ID for cross-device tracking
gtag('config', 'G-XXXXXXXXXX', {
'user_id': '{{@member.uuid}}'
});
{{/member}}
</script>
Event Tracking
Automatic Content Events
Track key content interactions:
<script>
// Track reading progress
let scrolled25 = false, scrolled50 = false, scrolled75 = false, scrolled100 = false;
window.addEventListener('scroll', function() {
let scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
if (scrollPercent >= 25 && !scrolled25) {
gtag('event', 'scroll_depth', {'percent': 25});
scrolled25 = true;
}
if (scrollPercent >= 50 && !scrolled50) {
gtag('event', 'scroll_depth', {'percent': 50});
scrolled50 = true;
}
if (scrollPercent >= 75 && !scrolled75) {
gtag('event', 'scroll_depth', {'percent': 75});
scrolled75 = true;
}
if (scrollPercent >= 100 && !scrolled100) {
gtag('event', 'scroll_depth', {'percent': 100});
scrolled100 = true;
}
});
</script>
Newsletter Signup Tracking
Track newsletter subscription events:
<script>
// Track newsletter signups
document.addEventListener('DOMContentLoaded', function() {
const subscribeForm = document.querySelector('.subscribe-form');
if (subscribeForm) {
subscribeForm.addEventListener('submit', function() {
gtag('event', 'newsletter_signup', {
'method': 'embedded_form',
'location': window.location.pathname
});
});
}
});
</script>
Social Share Tracking
Track social media sharing:
<script>
// Track social shares
document.querySelectorAll('.share-link').forEach(function(link) {
link.addEventListener('click', function() {
const platform = this.getAttribute('data-platform');
gtag('event', 'share', {
'method': platform,
'content_type': 'post',
'item_id': window.location.pathname
});
});
});
</script>
Membership & Subscription Tracking
Track Member Signups
Monitor new member registrations:
<script>
// Add to signup success page
{{#if success}}
gtag('event', 'sign_up', {
'method': 'Ghost'
});
{{/if}}
</script>
Track Subscription Purchases
For Ghost memberships and paid subscriptions:
<script>
// Add to subscription confirmation
{{#member}}
{{#if @member.paid}}
gtag('event', 'purchase', {
'transaction_id': '{{@member.subscriptions.id}}',
'value': {{@member.subscriptions.plan.amount}},
'currency': '{{@member.subscriptions.plan.currency}}',
'items': [{
'item_id': '{{@member.subscriptions.plan.id}}',
'item_name': '{{@member.subscriptions.plan.nickname}}',
'item_category': 'Membership',
'price': {{@member.subscriptions.plan.amount}}
}]
});
{{/if}}
{{/member}}
</script>
Troubleshooting
Tracking Not Working
Issue: No data appearing in GA4 reports
Solutions:
- Verify Measurement ID is correct (starts with G-)
- Check code injection was saved successfully
- Clear Ghost cache and browser cache
- Ensure tracking code is in Site Header, not Site Footer
- Test in incognito mode to rule out browser extensions
- Check browser console for JavaScript errors
Duplicate Page Views
Issue: Multiple page views recorded for single visit
Solutions:
- Check for multiple GA4 code snippets in different locations
- Verify theme doesn't include GA4 code separately
- Remove GA4 from custom theme files if using code injection
- Ensure GTM isn't also firing GA4 tags
Member Data Not Tracking
Issue: Member-specific events not appearing
Solutions:
- Verify member is logged in when testing
- Check member context helpers (
{{#member}}) are in correct location - Ensure Ghost version supports member features (3.0+)
- Test with GA4 DebugView for real-time validation
- Verify member UUID is being captured correctly
Code Injection Not Saving
Issue: Code injection changes don't persist
Solutions:
- Check for HTML syntax errors in injected code
- Verify you have admin permissions in Ghost
- Try injecting smaller code blocks first
- Clear Ghost cache:
ghost restart - Check Ghost logs for errors:
ghost log
Performance Issues
Issue: Site loading slower after GA4 installation
Solutions:
- Ensure
asyncattribute is present on script tag - Consider using Google Tag Manager for better performance
- Load GA4 script after critical content
- Implement consent mode to defer tracking until accepted
- Use Ghost's built-in caching and CDN features
Events Not Firing on Theme
Issue: Custom events not working with specific Ghost theme
Solutions:
- Check theme's JavaScript for conflicts
- Wrap event listeners in
DOMContentLoaded - Use theme-specific selectors for elements
- Add event code to theme files instead of code injection
- Update Ghost and theme to latest versions
Testing and Verification
Use GA4 DebugView
Enable debug mode for detailed event tracking:
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
</script>
Then check Admin > DebugView in GA4 for real-time events.
Browser Console Testing
Check console for successful tracking:
// View dataLayer contents
console.log(window.dataLayer);
// Manually fire test event
gtag('event', 'test_event', {'test_parameter': 'test_value'});
Ghost-Specific Testing
Test Ghost context data:
- View a post page - verify post metadata is captured
- Visit a page - verify page type is tracked
- Log in as member - verify member data is sent
- Subscribe to newsletter - verify signup event fires
- Check GA4 Real-time reports for all events