SyntaxCircus.Blazor.Tracking 0.1.1

dotnet add package SyntaxCircus.Blazor.Tracking --version 0.1.1
                    
NuGet\Install-Package SyntaxCircus.Blazor.Tracking -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SyntaxCircus.Blazor.Tracking" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SyntaxCircus.Blazor.Tracking" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="SyntaxCircus.Blazor.Tracking" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SyntaxCircus.Blazor.Tracking --version 0.1.1
                    
#r "nuget: SyntaxCircus.Blazor.Tracking, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SyntaxCircus.Blazor.Tracking@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SyntaxCircus.Blazor.Tracking&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=SyntaxCircus.Blazor.Tracking&version=0.1.1
                    
Install as a Cake Tool

SyntaxCircus.Blazor.Tracking

Build NuGet License: MIT

Consent-aware analytics building blocks for Blazor applications. The package bootstraps self-hosted Umami, direct GA4, or Google Tag Manager from configuration, and supplies an accessible consent UI that a host can completely style or replace.

No support guaranteed. Published as-is and maintained on a best-effort basis. This package supports privacy engineering; it does not provide legal advice or replace a host application's jurisdiction-specific review.

Install

dotnet add package SyntaxCircus.Blazor.Tracking

Register it in Program.cs:

builder.Services.AddSyntaxCircusTracking(builder.Configuration);

Import the components in the application's _Imports.razor:

@using SyntaxCircus.Blazor.Tracking.Components

Render the head bootstrap once, plus the banner and settings control in the body:

<head>
    <TrackingHead />
</head>
<body>
    <Routes />
    <ConsentBanner />
    <footer><ConsentSettings /></footer>
</body>

Configuration

All providers are disabled by default. An enabled provider must be complete or startup validation fails. Direct GA4 and GTM are alternative Google-provider modes: enable at most one.

{
  "Tracking": {
    "Consent": {
      "PolicyVersion": "1",
      "CookieName": "syntax_circus_privacy",
      "CookieLifetimeDays": 180,
      "PrivacyPolicyUrl": "/privacy"
    },
    "Umami": {
      "Enabled": false,
      "ScriptUrl": "https://analytics.example.com/script.js",
      "WebsiteId": "your-umami-website-id"
    },
    "GoogleAnalytics": {
      "Enabled": false,
      "MeasurementId": "G-XXXXXXXXXX"
    },
    "GoogleTagManager": {
      "Enabled": false,
      "ContainerId": "GTM-XXXXXXX"
    }
  }
}
Setting Purpose
Umami:Enabled Enables cookie-free Umami tracking.
Umami:ScriptUrl The URL of the self-hosted Umami tracker.
Umami:WebsiteId The Umami website ID.
GoogleAnalytics:Enabled Enables consent-gated GA4.
GoogleAnalytics:MeasurementId GA4 measurement ID, normally supplied through deployment configuration.
GoogleTagManager:Enabled Enables consent-gated Google Tag Manager. Mutually exclusive with GoogleAnalytics:Enabled.
GoogleTagManager:ContainerId GTM web-container ID.
Consent:PolicyVersion Invalidates a previous choice when privacy policy changes.
Consent:CookieName Essential first-party preference cookie name.
Consent:CookieLifetimeDays Preference-cookie lifetime, from 1 to 400 days.
Consent:PrivacyPolicyUrl Optional link shown in the default banner.

Startup validation requires a direct-GA4 MeasurementId in G-… format and a GTM ContainerId in GTM-… format. It rejects a configuration that enables both modes.

Umami only

Enable Umami with a tracker you operate. It is loaded on every visit and does not create the consent banner because it uses no visitor cookie.

"Umami": {
  "Enabled": true,
  "ScriptUrl": "https://analytics.example.com/script.js",
  "WebsiteId": "b2a6af60-8ca1-4c92-a931-5d1d6ec9201d"
}

Direct GA4

Enable GA4 only when the host is ready to request consent. The package uses Basic Consent Mode: it queues denied defaults locally, but does not load gtag.js, send a Google request, or set a GA cookie until analytics consent is given. Marketing consent controls ad_storage, ad_user_data, and ad_personalization; analytics consent controls analytics_storage.

"GoogleAnalytics": {
  "Enabled": true,
  "MeasurementId": "G-XXXXXXXXXX"
}

Use deployment secrets/environment variables rather than committing production IDs when that is your team's policy:

Tracking__GoogleAnalytics__Enabled=true
Tracking__GoogleAnalytics__MeasurementId=G-XXXXXXXXXX

Google Tag Manager

Use GTM when your organization centrally manages Google, third-party, or custom tags. It is a separate mode: do not enable it alongside direct GA4 and do not add the same container manually elsewhere in the host.

"GoogleTagManager": {
  "Enabled": true,
  "ContainerId": "GTM-XXXXXXX"
}

The package uses Basic Consent Mode for the container: it queues denied Google consent defaults locally, then loads gtm.js only after analytics or marketing consent is granted. A reject-all choice makes no Google request. Direct GA4 loads only after analytics consent.

After GTM starts, and on every later preference change, the package pushes this fixed dataLayer event:

{
  event: "syntax_circus_consent_update",
  syntaxCircusConsent: { analytics: true, marketing: false }
}

The syntax_circus_ prefix deliberately identifies the package and avoids collisions with host-defined GTM events. It is an integration hook, not visitor-facing data; a host only encounters it when it enables GTM. Configure every GTM tag and trigger to respect the relevant Google consent state and, where needed, the event above.

The package initializes Google Consent Mode with all four supported states denied. On a saved or changed choice it sends a consent update, then starts the configured provider only when that choice permits it. A policy-version change invalidates the stored choice and shows the banner again.

When a user withdraws consent, the package updates the loaded Google tag or container to denied and makes a best-effort removal of known first-party Google cookies on the current host and parent domains:

  • Analytics: _ga, _ga_*, _gid, _gat*, and _dc_gtm_*.
  • Marketing: _gac_* and _gcl_*.

It cannot unload a script already executing on the page, infer a cookie written on an unknown path/domain, or remove cookies created by third-party or custom GTM tags. GTM container owners must configure those tags and their cleanup policies themselves.

The package ships semantic defaults but no CSS. Style the default classes and hooks in the host:

.syntax-circus-consent-banner { position: fixed; inset: auto 1rem 1rem; }
.syntax-circus-consent-settings { text-decoration: underline; }

Replace individual regions when only copy or layout changes:

<ConsentBanner CssClass="my-consent-card">
    <HeaderContent><h2>Privacy, on your terms</h2></HeaderContent>
    <ActionsContent>
        <button type="button" data-privacy-action="accept-all">Allow analytics</button>
        <button type="button" data-privacy-action="reject-all">No thanks</button>
        <button type="button" data-privacy-action="open-settings">Choose settings</button>
        <button type="button" data-privacy-action="save-settings" hidden>Save</button>
    </ActionsContent>
</ConsentBanner>

ChildContent replaces all default markup. Preserve data-privacy-banner on the outer element and use the action/category hooks below so the package JavaScript can perform the choice.

<ConsentBanner>
    <ChildContent>
        <aside class="my-consent-card" data-privacy-banner hidden role="dialog" aria-label="Privacy choices">
            <h2>Choose optional tracking</h2>
            <label><input type="checkbox" data-privacy-category="analytics" /> Analytics</label>
            <label><input type="checkbox" data-privacy-category="marketing" /> Marketing</label>
            <button type="button" data-privacy-action="save-settings">Save choices</button>
            <button type="button" data-privacy-action="reject-all">Reject all</button>
        </aside>
    </ChildContent>
</ConsentBanner>
Hook Behavior
data-privacy-banner Banner root that can be displayed, hidden, and queried for choices.
data-privacy-action="accept-all" Saves analytics and marketing consent.
data-privacy-action="reject-all" Saves rejection of optional categories.
data-privacy-action="open-settings" Reveals the default settings controls and banner.
data-privacy-action="save-settings" Saves category checkboxes.
data-privacy-category="analytics" Analytics checkbox.
data-privacy-category="marketing" Marketing checkbox.
data-privacy-settings-link A persistent control that opens preferences.

Host responsibilities

  • Write the privacy notice and decide where it is linked.
  • Choose the countries in which consent is shown; this package applies the configured policy globally.
  • Keep the dashboard, database, backup policy, and provider credentials secure.
  • Do not add direct GA4, the configured GTM container, tracking pixels, or vendor scripts outside TrackingHead, or they can bypass consent or duplicate page views.
  • Govern GTM publishing carefully: a container can add third-party or custom scripts without a package release, and those tags must carry their own consent requirements and cookie-cleanup policy.
  • Treat syntax_circus_consent_update and its syntaxCircusConsent payload as a stable, fixed package integration contract when configuring GTM triggers or variables.
  • Test browser cookies and network traffic after every provider/configuration change.

Validation

dotnet restore SyntaxCircus.Blazor.Tracking.slnx
dotnet build SyntaxCircus.Blazor.Tracking.slnx --configuration Release
pwsh tests/SyntaxCircus.Blazor.Tracking.BrowserTests/bin/Release/net10.0/playwright.ps1 install chromium
dotnet test SyntaxCircus.Blazor.Tracking.slnx --no-build --configuration Release
dotnet pack SyntaxCircus.Blazor.Tracking.slnx --no-build --configuration Release

Verify a Google deployment in browser developer tools: before an applicable choice there must be no request to googletagmanager.com and no Google cookie. After a choice, confirm only the consented provider runs; after revocation, confirm the provider receives denied consent and known Google cookies are removed.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 161 8/22/2026
0.1.0 103 8/22/2026