Umbraco provides multiple methods to implement Google Analytics 4, each suited to different .NET environments and deployment strategies. This guide covers NuGet package integration, manual Razor implementation, and IIS-level configuration.
Prerequisites
Before implementing GA4 on Umbraco:
- GA4 Property Created - Set up your GA4 property in Google Analytics
- Measurement ID - Obtain your GA4 measurement ID (format:
G-XXXXXXXXXX) - Umbraco Backoffice Access - Administrator access required
- Development Environment - Visual Studio 2022+ or VS Code with C# extension
- .NET Version - .NET 6.0+ (Umbraco 10+) or .NET Framework 4.7.2+ (Umbraco 8)
Method 1: Razor View Integration (Recommended)
Direct Razor integration provides maximum control and works across all Umbraco versions.
Step 1: Create Analytics Partial View
Navigate to your Umbraco project and create a partial view:
Location: /Views/Partials/Analytics/GoogleAnalytics.cshtml
@using Umbraco.Cms.Core.Models.PublishedContent
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@{
var gaId = "G-XXXXXXXXXX"; // Replace with your GA4 Measurement ID
// Get GA ID from Umbraco settings if stored in config
if (Model?.Value<string>("googleAnalyticsId") != null)
{
gaId = Model.Value<string>("googleAnalyticsId");
}
}
@if (!string.IsNullOrEmpty(gaId))
{
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=@gaId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@gaId', {
'send_page_view': true,
'custom_map': {
'dimension1': 'content_type',
'dimension2': 'node_id',
'dimension3': 'template_name'
}
});
// Send Umbraco-specific data
gtag('event', 'page_view', {
'content_type': '@Model?.ContentType.Alias',
'node_id': '@Model?.Id',
'template_name': '@ViewData.GetType()?.Name'
});
</script>
}
Step 2: Include Partial in Master Layout
Edit your master layout template (usually Master.cshtml or Layout.cshtml):
Location: /Views/Master.cshtml
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@Model?.Value("title") ?? Model?.Name</title>
@* Include Google Analytics before closing head *@
@await Html.PartialAsync("~/Views/Partials/Analytics/GoogleAnalytics.cshtml")
@RenderSection("head", required: false)
</head>
<body>
@RenderBody()
</body>
</html>
Step 3: Configure GA4 ID in Umbraco Settings
Option A: Store in appsettings.json
{
"Analytics": {
"GoogleAnalytics": {
"MeasurementId": "G-XXXXXXXXXX",
"Enabled": true
}
}
}
Access in Razor:
@inject IConfiguration Configuration
@{
var gaId = Configuration["Analytics:GoogleAnalytics:MeasurementId"];
}
Option B: Create Document Type Property
- Navigate to Settings → Document Types
- Select your Site Settings document type (or create one)
- Add new property:
- Name: Google Analytics Measurement ID
- Alias: googleAnalyticsId
- Editor: Textstring
- Save document type
- Navigate to Content → Site Settings
- Enter your GA4 Measurement ID
- Save and publish
Step 4: Verify Installation
- Build and run your Umbraco site
- Open your site in a browser
- Open DevTools → Network tab
- Look for requests to
google-analytics.comorgoogletagmanager.com - Check Google Analytics → Realtime to see active users
Method 2: NuGet Package Integration
Use community packages for simplified GA4 integration.
Step 1: Install NuGet Package
Via Package Manager Console:
Install-Package Our.Umbraco.GoogleAnalytics
Via .NET CLI:
dotnet add package Our.Umbraco.GoogleAnalytics
Via Package Reference (.csproj):
<ItemGroup>
<PackageReference Include="Our.Umbraco.GoogleAnalytics" Version="3.*" />
</ItemGroup>
Step 2: Configure Package
Register in Startup.cs/Program.cs:
For Umbraco 10+ (.NET 6+):
using Our.Umbraco.GoogleAnalytics.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.CreateUmbracoBuilder()
.AddBackOffice()
.AddWebsite()
.AddDeliveryApi()
.AddComposers()
.AddGoogleAnalytics() // Add this line
.Build();
var app = builder.Build();
// ... rest of configuration
For Umbraco 8 (.NET Framework):
using Our.Umbraco.GoogleAnalytics;
public class ApplicationEventHandler : IApplicationEventHandler
{
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication,
ApplicationContext applicationContext)
{
GoogleAnalyticsHelper.Register();
}
}
Step 3: Configure in Backoffice
- Log in to Umbraco Backoffice (
/umbraco) - Navigate to Settings → Google Analytics
- Enter Measurement ID:
G-XXXXXXXXXX - Select Tracking Type: GA4
- Enable Enhanced Measurement (optional)
- Click Save
Step 4: Test Implementation
Method 3: IIS Application-Level Integration
Implement GA4 at IIS level for all applications in a site.
Step 1: Create IIS HTTP Module
Create a custom HTTP module:
GoogleAnalyticsModule.cs:
using System;
using System.Web;
namespace YourProject.Modules
{
public class GoogleAnalyticsModule : IHttpModule
{
private const string MeasurementId = "G-XXXXXXXXXX";
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
}
private void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpContext context = app.Context;
// Only inject for HTML responses
if (context.Response.ContentType == "text/html")
{
context.Response.Filter = new GoogleAnalyticsFilter(
context.Response.Filter,
MeasurementId
);
}
}
public void Dispose() { }
}
public class GoogleAnalyticsFilter : System.IO.Stream
{
private System.IO.Stream _responseStream;
private string _measurementId;
public GoogleAnalyticsFilter(System.IO.Stream stream, string measurementId)
{
_responseStream = stream;
_measurementId = measurementId;
}
public override void Write(byte[] buffer, int offset, int count)
{
string html = System.Text.Encoding.UTF8.GetString(buffer, offset, count);
// Inject GA4 before </head>
string gaScript = $@"
<script async src='https://www.googletagmanager.com/gtag/js?id={_measurementId}'></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){{dataLayer.push(arguments);}}
gtag('js', new Date());
gtag('config', '{_measurementId}');
</script>
";
html = html.Replace("</head>", gaScript + "</head>");
byte[] data = System.Text.Encoding.UTF8.GetBytes(html);
_responseStream.Write(data, 0, data.Length);
}
// Implement required abstract members
public override bool CanRead => _responseStream.CanRead;
public override bool CanSeek => _responseStream.CanSeek;
public override bool CanWrite => _responseStream.CanWrite;
public override long Length => _responseStream.Length;
public override long Position
{
get => _responseStream.Position;
set => _responseStream.Position = value;
}
public override void Flush() => _responseStream.Flush();
public override int Read(byte[] buffer, int offset, int count)
=> _responseStream.Read(buffer, offset, count);
public override long Seek(long offset, System.IO.SeekOrigin origin)
=> _responseStream.Seek(offset, origin);
public override void SetLength(long value)
=> _responseStream.SetLength(value);
}
}
Step 2: Register Module in Web.config
For IIS Integrated Pipeline:
<configuration>
<system.webServer>
<modules>
<add name="GoogleAnalyticsModule"
type="YourProject.Modules.GoogleAnalyticsModule, YourProject" />
</modules>
</system.webServer>
</configuration>
For IIS Classic Pipeline:
<configuration>
<system.web>
<httpModules>
<add name="GoogleAnalyticsModule"
type="YourProject.Modules.GoogleAnalyticsModule, YourProject" />
</httpModules>
</system.web>
</configuration>
Enhanced Umbraco Integration
Track Umbraco Member Data
@using Umbraco.Cms.Core.Security
@inject IMemberManager MemberManager
@{
var member = await MemberManager.GetCurrentMemberAsync();
}
<script>
@if (member != null)
{
<text>
gtag('set', 'user_properties', {
'member_status': 'logged_in',
'member_id': '@member.Key',
'member_type': '@member.ContentType.Alias'
});
</text>
}
else
{
<text>
gtag('set', 'user_properties', {
'member_status': 'visitor'
});
</text>
}
</script>
Track Content Type and Template
<script>
gtag('event', 'page_view', {
'content_type': '@Model?.ContentType.Alias',
'content_name': '@Model?.Name',
'template_alias': '@ViewData["TemplateAlias"]',
'node_path': '@Model?.Path',
'culture': '@Model?.GetCultureFromDomains()?.Culture'
});
</script>
Track Umbraco Forms Submissions
@using Umbraco.Forms.Core.Services
@inject IFormService FormService
<script>
// Track form submission
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('form.umbraco-forms-form').forEach(function(form) {
form.addEventListener('submit', function(e) {
gtag('event', 'form_submit', {
'form_id': form.getAttribute('data-form-id'),
'form_name': form.getAttribute('data-form-name')
});
});
});
});
</script>
Performance Optimization
Preconnect to GA4 Domains
<head>
<link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
@await Html.PartialAsync("~/Views/Partials/Analytics/GoogleAnalytics.cshtml")
</head>
Defer Loading for Better LCP
<script>
// Defer GA4 until after page load
window.addEventListener('load', function() {
var script = document.createElement('script');
script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX';
script.async = true;
document.head.appendChild(script);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
});
</script>
Conditional Loading by Environment
@inject Microsoft.Extensions.Hosting.IHostEnvironment HostEnvironment
@if (HostEnvironment.IsProduction())
{
@await Html.PartialAsync("~/Views/Partials/Analytics/GoogleAnalytics.cshtml")
}
IIS-Specific Configuration
Enable IIS Compression for Analytics Scripts
In Web.config:
<configuration>
<system.webServer>
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
<httpCompression>
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
<dynamicTypes>
<add mimeType="application/javascript" enabled="true" />
<add mimeType="text/javascript" enabled="true" />
</dynamicTypes>
<staticTypes>
<add mimeType="application/javascript" enabled="true" />
</staticTypes>
</httpCompression>
</system.webServer>
</configuration>
Set Cache Headers for GA4 Scripts
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
</staticContent>
</system.webServer>
</configuration>
Validation and Testing
Debug Mode
Enable GA4 debug mode for development:
@inject Microsoft.Extensions.Hosting.IHostEnvironment HostEnvironment
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': @(HostEnvironment.IsDevelopment() ? "true" : "false")
});
</script>
Real-Time Testing
- Open Google Analytics → Realtime → Overview
- Navigate your Umbraco site in incognito mode
- Verify events appear in real-time report
- Check custom dimensions populate correctly
Browser DevTools Testing
- Open DevTools → Console
- Type:
dataLayerand press Enter - Verify data layer contains expected events
- Check for GA4 errors in console
Common Issues
GA4 Not Tracking
Cause: Incorrect Measurement ID Solution:
- Verify
G-XXXXXXXXXXformat in Razor view - Check for typos in configuration
- Ensure ID matches GA4 property
Duplicate Tracking
Cause: Multiple GA4 implementations Solution:
- Search solution for duplicate
gtagreferences - Remove redundant partial views
- Check for package + manual implementation conflict
Umbraco Cache Preventing Updates
Cause: Umbraco cache not cleared after code changes Solution:
# Clear Umbraco cache
Remove-Item -Path "App_Data\TEMP\*" -Recurse -Force
# Or use Umbraco Backoffice
# Settings → Examine Management → Rebuild all indexes
IIS Module Not Firing
Cause: Module not registered or wrong pipeline mode Solution:
- Verify
Web.configregistration - Check IIS application pool mode (Integrated vs Classic)
- Restart IIS:
iisreset
Next Steps
- Google Analytics Event Tracking - Track Umbraco-specific events
- Google Analytics E-commerce - Track Vendr/Umbraco Commerce transactions
- Troubleshooting Tracking Issues - Debug GA4 problems