Adding Privacy-First Analytics to Angular Apps in 2026
Angular apps have specific needs when it comes to analytics. The framework’s strong opinions about dependency injection, routing, and initialization mean you want analytics that fits cleanly into Angular’s architecture — not a script tag bolted onto index.html and forgotten.
Measure.events is 900 bytes, zero cookies, no consent banners required, and ships with an MCP server so tools like Cursor and Claude Code can query your analytics directly. Here’s how to wire it into an Angular 17/18 app properly.
Option 1: Simple Script Injection via index.html
The fastest way to get started. Add the script to your src/index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Angular App</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script
defer
data-site-key="YOUR_SITE_KEY"
src="https://lets.measure.events/api/script/YOUR_SITE_KEY"
></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
This captures initial page loads. For SPA route changes, keep reading.
Option 2: APP_INITIALIZER (The Angular Way)
For cleaner integration that plays well with Angular’s lifecycle, use APP_INITIALIZER in your app.config.ts:
// app.config.ts
import { ApplicationConfig, APP_INITIALIZER } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
function initMeasure(): () => void {
return () => {
if (typeof document !== 'undefined') {
const script = document.createElement('script');
script.defer = true;
script.dataset['siteKey'] = 'YOUR_SITE_KEY';
script.src = 'https://lets.measure.events/api/script/YOUR_SITE_KEY';
document.head.appendChild(script);
}
};
}
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
{
provide: APP_INITIALIZER,
useFactory: initMeasure,
multi: true,
},
],
};
This approach works properly with Angular Universal (SSR) since the typeof document check prevents the script from running on the server.
SPA Pageview Tracking with Angular Router
Angular is a single-page app framework — navigating between routes doesn’t reload the page, so the script won’t auto-fire a pageview on each navigation. You need to listen to NavigationEnd events from the Router.
Create a tracking service:
// analytics.service.ts
import { Injectable, OnDestroy } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
declare global {
interface Window {
measure?: {
track: (event: string, data?: Record<string, unknown>) => void;
trackPageview: (data?: { path?: string }) => void;
};
}
}
@Injectable({ providedIn: 'root' })
export class AnalyticsService implements OnDestroy {
private routerSub: Subscription;
constructor(private router: Router) {
this.routerSub = this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
window.measure?.trackPageview({ path: event.urlAfterRedirects });
});
}
track(event: string, data: Record<string, unknown> = {}): void {
window.measure?.track(event, data);
}
ngOnDestroy(): void {
this.routerSub.unsubscribe();
}
}
Then inject it into your root component so it starts listening on app boot:
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { AnalyticsService } from './analytics.service';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: '<router-outlet />',
})
export class AppComponent {
// Inject to initialize — Angular's DI keeps it alive
constructor(private analytics: AnalyticsService) {}
}
Custom Event Tracking
The AnalyticsService handles custom events too. Use it anywhere in your app:
// feature.component.ts
import { Component } from '@angular/core';
import { AnalyticsService } from '../analytics.service';
@Component({
selector: 'app-pricing',
template: `
<button (click)="onSignupClick()">Start free trial</button>
`,
})
export class PricingComponent {
constructor(private analytics: AnalyticsService) {}
onSignupClick(): void {
this.analytics.track('signup_click', { source: 'pricing_page' });
}
}
Track form submissions, feature usage, checkout steps — anything meaningful to your product:
// Common tracking patterns
this.analytics.track('form_submit', { form: 'contact' });
this.analytics.track('plan_selected', { plan: 'pro', billing: 'annual' });
this.analytics.track('feature_used', { feature: 'export', format: 'csv' });
900 Bytes vs GA4’s 45KB
Angular developers care about bundle size. You’ve carefully optimized your lazy-loaded routes, tuned your bundle analyzer, and set up tree-shaking. Then GA4 loads 45KB of JavaScript you have no control over.
Measure.events loads 900 bytes — about the same size as a small PNG icon. The performance math is simple:
- GA4
gtag.js: ~45,000 bytes, blocks rendering if not deferred - Measure.events: ~900 bytes, always deferred
For Angular apps targeting Lighthouse scores above 90, GA4 is the analytics equivalent of importing all of lodash.
MCP Server: Query Your Angular App’s Traffic from Claude or Cursor
This is where Measure.events earns its differentiation in 2026. Once you have the MCP server configured, your AI coding tools become analytics dashboards.
In Cursor or Claude Code, you can ask:
“What are the top 5 pages by traffic this week?” “Which referrers drove traffic to /pricing?” “Did the Angular 18 migration change bounce rates?”
No other analytics tool offers a native MCP integration. Amplitude, Mixpanel, and GA4 all require you to leave your editor and click through a dashboard. Measure.events answers in the same context you’re already working in.
Install the MCP server:
npx -y @measure-events/mcp
Then add it to your Cursor or Claude Code config:
{
"mcpServers": {
"measure": {
"command": "npx",
"args": ["-y", "@measure-events/mcp"],
"env": {
"MEASURE_API_KEY": "your_api_key"
}
}
}
}
Getting Started
- Sign up at lets.measure.events/sign-up
- Create a site and copy your site key
- Add the script via
index.htmlorAPP_INITIALIZER - Drop in the
AnalyticsServicefor Router-based SPA tracking - Optional: install the MCP server for AI-powered queries in Cursor/Claude Code
Setup time: under 10 minutes. Bytes added to your Angular bundle: 0 (the script loads externally with defer).
Ready to see accurate analytics?
No cookies. No consent banners. No personal data. $29/mo with a 14-day free trial.
Start free trial →