eQuantic.UIeQuantic.UI
Docs
Playground
GitHub
START HERE
Getting Started
WRITE-ONCE
Write-Once Components
Declarative Surface
Photon Engine
Design System
Capabilities
Storage
Forms
Code Editor
Markdown
Mermaid
Email Rendering
ARCHITECTURE
Architecture Overview
Package Architecture
Components
Styling
Localization
Analytics & GTM
COMPILATION
Compiler
Compile-Time Evaluation
Supported C# Features
External Type Resolution
Build Flow
Diagnostics
RUNTIME
Runtime (TypeScript)
Performance
SERVER
Server Integration
Assets
BunPackage
Security
ECOSYSTEM
Image
Charts
Icons
Lottie
DEVELOPMENT
Visual Editor
Debug
Roadmap
PT-BR
Analytics-pt-BR
Architecture-pt-BR
Assets-pt-BR
BuildFlow-pt-BR
BunPackage-pt-BR
Capabilities-pt-BR
Charts-pt-BR
CodeEditor-pt-BR
Compiler-pt-BR
CompileTimeEvaluation-pt-BR
Components-pt-BR
Debug-pt-BR
DeclarativeSurface-pt-BR
DesignSystem-pt-BR
Diagnostics-pt-BR
EmailRealizer-pt-BR
ExternalTypeResolution-pt-BR
Forms-pt-BR
GettingStarted-pt-BR
Home-pt-BR
Icons-pt-BR
Image-pt-BR
Localization-pt-BR
Lottie-pt-BR
Markdown-pt-BR
Mermaid-pt-BR
PackageArchitecture-pt-BR
Performance-pt-BR
Photon-pt-BR
Roadmap-pt-BR
Runtime-pt-BR
Security-pt-BR
ServerIntegration-pt-BR
Storage-pt-BR
Styling-pt-BR
SupportedFeatures-pt-BR
VisualEditor-pt-BR
WriteOnceComponents-pt-BR
ACTIONS & INPUTS
Button
IconButton
TextInput
Select
Checkbox
Switch
RadioGroup
SegmentedControl
Slider
Stepper
SearchField
SURFACES & DISPLAY
Card
Badge
Chip
Avatar
Banner
ProgressBar
EmptyState
Divider
NAVIGATION
Tabs
AppBar
BottomNavigation
Breadcrumb
Pagination
PageIndicator
Menu
Drawer
OVERLAYS
Dialog
BottomSheet
Toast
Popover
Tooltip
LISTS & DATA
List
ListView
Table
Accordion
CodeBlock
TOUCH INTERACTION
PullToRefresh
SwipeableRow
DocsArchitecture
Analytics & Google Tag Manager
Edit this page
4 min read
🌐 This page in: English · Português
Analytics in eQuantic.UI is a capability, not a script you paste: a page asks for IAnalytics the way it asks for a camera, and never learns who is listening. The eQuantic.UI.Gtm package is an INSTALLER: one call wires a Google Tag Manager container to that capability, end to end.
Since 0.2.0-preview.29
Install
1
2
3
4
5
using eQuantic.UI.Gtm;
builder.Services.AddUI(options => options
.ScanAssembly(typeof(Program).Assembly)
.UseGtm("GTM-XXXXXXX"));
That one call installs three things into the HTML shell:
1.
The official container snippet: the same bytes Google documents, parameterized only where GTM itself templates them.
2.
The installer declaration (window.__EQ_ANALYTICS__): what arms the runtime's IAnalytics realization. Without an installer, tracking is a silent no-op by design.
3.
SPA page views: the client router announces every committed navigation (eq:navigate), and the shell turns it into page_view pushes carrying page_path and page_title, GA4's own field names. The container sees the initial load by itself; these are the navigations it cannot see.
The container id is validated at startup: a typo'd id installs a container that silently collects nothing, and that is discovered in next month's empty report, so refusing early is the kinder failure.
Tracking from a page
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public override VisualNode Build(ComponentContext context)
{
_analytics = context.GetService<IAnalytics>();
// …
}
private async Task Submit()
{
// …after the server said yes:
_analytics?.Track("sign_up");
_analytics?.Track("purchase", new Dictionary<string, object?>
{
["value"] = 42,
["currency"] = "EUR",
});
}
Track is fire-and-forget by contract, because analytics must never make a page wait. Event names are YOUR vocabulary (sign_up, begin_checkout); the framework never invents or prefixes any. On the server the same call is a no-op: SSR is not a user, and a page that tracked during rendering would count its own crawlers.
One container per app, on purpose
A GTM container loads into the DOCUMENT and never unloads, so in a SPA a "per-page container" cannot exist. Per-route variation is what the container's own triggers are for: the automatic page_view carries page_path precisely so marketing can fire tags per route without the app changing, which is the entire point of a tag manager.
What does exist is the agency-plus-client case: call UseGtm once per container. The second call adds only its snippet; both ride the same dataLayer (GTM's own multi-container rule, enforced at startup).
Options
1
2
3
4
5
.UseGtm("GTM-XXXXXXX", gtm => gtm
.WithDataLayerName("eqData") // when another script already owns `dataLayer`
.WithoutSpaPageViews() // container uses GA4's history trigger instead
.WithEnvironment("auth…", "env-9") // GTM environments (gtm_auth / gtm_preview)
.WithConsent()) // load the container only after the visitor consents
Turn WithoutSpaPageViews() on when the container tracks history changes itself, or the same navigation counts twice.
Consent (GDPR / LGPD)
STATUS: BUILT; SHIPS WITH THE NEXT RELEASE AFTER 0.2.0-PREVIEW.45
Both regulations say the same thing about analytics cookies: nothing is set until the visitor says yes, and a "no" is remembered. Three pieces make that true without the app writing a line of JavaScript:
IConsent — a capability like any other (context.GetService<IConsent>()): State is Unknown, Granted or Denied; Grant() / Deny() store the answer. On the web it lives in ONE cookie, eq-consent, for a year, and a change is announced on the document as eq:consent. On the server the same cookie is READ from the request, so a returning visitor's first paint has no banner in it — the SSR pass knows the answer the browser holds.
CookieConsent(policyHref) — the card that asks. Drawn only while the answer is unknown, gone the moment it is given; two buttons (accept / decline) and the privacy-policy link, all localized (EN, pt-BR, es) and each overridable. It is a card, not a layer: put it where the shell wants it (a Sticky at the bottom of the page is the usual home).
UseGtm(id, gtm => gtm.WithConsent()) — the installer gates the container on the answer. The head declares Google Consent Mode defaults of denied, and the container script is fetched only when the cookie says granted, or the moment eq:consent says so. A visitor who declines or never answers downloads no tag manager at all. Track() calls made in the meantime sit in the dataLayer array, which the container replays if consent arrives later.
1
2
3
4
5
builder.Services.AddUI(o => o.ScanAssembly(typeof(Program).Assembly)
.UseGtm("GTM-XXXXXXX", gtm => gtm.WithConsent()));
// somewhere every page renders, at the bottom of its shell:
Sticky(CookieConsent("/privacy"))
Off by default: an installer that already ran without asking keeps running. Turning it on is the app's decision, paired with the card and a policy page behind the link — a consent card without a policy behind it is a promise the site cannot keep.
What is deliberately absent
The <noscript> iframe from Google's install instructions. It measures users whose browsers run no JavaScript, and such a user gets no app at all here: there is nothing to measure.
A native realization. IAnalytics resolves to a no-op in a Photon window today; the mobile analytics bridges join with the native track.
Related
Server Integration: AddUI, the shell, and where UseGtm hangs.
Capabilities: the ask-by-interface pattern IAnalytics follows.