Product

Session Replay and Heatmaps for Angular Apps

Add session replay and heatmaps to an Angular application. Covers installing the loader in index.html, automatic Angular Router tracking, identifying users from a service, custom events, masking, and Angular-specific pitfalls.

Purushottam Kumar Suman
Purushottam Kumar SumanSeptember 30, 20266 min read
Founder & CEO, DeepSync
Angular application code with analytics setup

Short answer: add the loader script tag to src/index.html. Angular Router navigations are captured automatically through the History API, so you do not need to subscribe to router events. Wrap SDK calls in a small service for identifying users and tracking events, and add masking classes in your templates for anything sensitive.

Table of Contents

  1. Install
  2. Routing
  3. A Thin Service for SDK Calls
  4. Masking in Templates
  5. Angular-Specific Things to Check
  6. Debugging With Replay
  7. Key Takeaways

Install

<!-- src/index.html -->
<head>
  <script async src="YOUR_LOADER_URL"></script>
</head>

Your loader URL is in the project's install settings. Because the tag is in the HTML shell, recording starts before Angular bootstraps. Reference: Install in Angular, Svelte, or SvelteKit.

Routing

The SDK patches pushState and replaceState. Every navigation through the Angular Router is recorded as a page view with no code from you.

  • Lazy-loaded routes are tracked the same way.
  • Route parameters appear with real values in the URL.
  • Named outlets and auxiliary routes change the URL and are recorded.

If you use hash-based routing, check that routes appear as separate pages in your session timeline after installing.

Views that change without a URL change, such as a stepper or tabs, need a custom event.

A Thin Service for SDK Calls

Keeping SDK calls in one place makes them easy to test and to change.

import { Injectable } from "@angular/core";

@Injectable({ providedIn: "root" })
export class AnalyticsService {
  private call(...args: unknown[]) {
    const ds = (window as any).deepsync;
    if (typeof ds === "function") ds(...args);
  }

  identify(id: string, traits: Record<string, unknown>) {
    this.call("identify", id, traits);
  }

  track(event: string, props?: Record<string, unknown>) {
    this.call("track", event, props);
  }
}

Use it after login and at confirmed outcomes:

this.analytics.identify(user.id, { plan: user.plan });
this.analytics.track("report_exported");

The guard on window.deepsync avoids errors if the script is blocked or has not loaded. If calls may run before the loader finishes, add the command-queue stub from how the SDK works so they are buffered instead of dropped.

With server-side rendering, call these only in the browser.

Masking in Templates

Inputs, passwords, and payment fields are masked by default. For rendered data:

<td class="ds-mask">{{ customer.email }}</td>

<section class="ds-block">
  <app-bank-details [account]="account"></app-bank-details>
</section>

Angular's view encapsulation does not affect this. The classes are read from the DOM by the recorder (PII masking).

In consent-required mode, call the consent function when your banner is accepted (consent modes).

Angular-Specific Things to Check

  • Shadow DOM encapsulation. Components using ViewEncapsulation.ShadowDom render inside shadow roots. Open roots can be captured; check a recording of those components (capture limits).
  • Component libraries with overlays. Dialogs, menus, and select panels render in an overlay container attached to the body. They are part of the DOM and recorded, and appear in replay as they did for the user.
  • Change detection and mutations. Recording observes the DOM directly. Heavy re-rendering of large tables produces more data, so virtual scrolling is worth using.
  • Zone and third-party scripts. The loader is external and asynchronous and does not need to run inside Angular's zone.
  • Content Security Policy. Allow the ingest origin in script-src and connect-src (CSP guide).
  • Enterprise screens. Angular apps are common in internal and regulated products. Audit masking on screens that display customer records before recording.

Debugging With Replay

Angular errors in production are often minified and hard to interpret. Replays give the context.

  1. Open JavaScript Errors, ranked by sessions affected.
  2. Open a session where an error occurred.
  3. Read the sequence: the user's click, the network request, the response, the console error.
  4. Reproduce locally with the same route, browser, and viewport.

A common production-only fault is a lazy-loaded chunk failing after a deploy, when a user with an old tab navigates to a route whose file no longer exists. It shows clearly as a navigation followed by a loading error (reproducing bugs).

Key Takeaways

  • One script tag in src/index.html.
  • Router navigation is tracked automatically.
  • Put identify and track calls in a small service.
  • Mask rendered data in templates and audit data-heavy screens.

Conclusion

Angular apps are often large and long-lived, with users who cannot easily describe what went wrong. A recording of the session does it for them.

Start free · Install in Angular

Frequently Asked Questions

Was this article helpful?

Ready to understand
users like never before?

Join thousands of teams who use DeepSync to uncover insights,improve experiences, and build better products—faster.

Quick & easy onboarding
See results in real time
Enterprise-grade security