Angular integration
CDP is a React micro frontend. In an Angular host you do not rewrite it — you embed
the pre-built remote and drive it through the @segmentify/common host bridge. Two paths
are supported:
| Path | Recommendation |
|---|---|
| iframe + postMessage | Recommended for most Angular hosts. Works with any Angular version, strong isolation, no React in your bundle. |
Module Federation mount |
Advanced. Deeper UI integration, but you load a React remote into the Angular runtime and must align shared deps. |
Path A — iframe (recommended)
Section titled “Path A — iframe (recommended)”Template
Section titled “Template”<iframe #cdpFrame [src]="safeSrc" style="width: 100%; height: 800px; border: 0"></iframe>Component + service
Section titled “Component + service”import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from "@angular/core";import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";import { Router } from "@angular/router";import { bindPostMessageHost } from "@segmentify/common";
const CDP_ORIGIN = "https://cdp.segmentify.com";
@Component({ selector: "app-cdp-embed", templateUrl: "./cdp-embed.component.html",})export class CdpEmbedComponent implements OnInit, OnDestroy { @ViewChild("cdpFrame", { static: true }) frame!: ElementRef<HTMLIFrameElement>;
safeSrc: SafeResourceUrl; private controller?: ReturnType<typeof bindPostMessageHost>;
constructor( private sanitizer: DomSanitizer, private router: Router, private auth: AuthService // your token/account source ) { this.safeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(`${CDP_ORIGIN}/embed.html`); }
ngOnInit(): void { this.controller = bindPostMessageHost( this.frame.nativeElement, { environment: "production", hostBaseUrl: "https://sauron.segmentify.com", cdpBaseUrl: "https://api.segmentify.com", // required for CDP authToken: this.auth.getBearerToken(), hostData: { user: { user: { account: { apiKey: this.auth.getApiKey(), dataCenter: "gc", mainCurrency: { code: "USD", symbol: "$" }, }, lang: "en", }, }, }, requestHeaders: { "X-Switch-User": this.auth.getSwitchUser(), "X-Switch-Region": "ALL", }, }, { targetOrigin: CDP_ORIGIN, onReady: () => console.log("CDP ready"), onNavigate: (path) => this.router.navigateByUrl(path), onError: (message) => console.error("CDP error:", message), } ); }
// Call when the user's token is refreshed refreshToken(token: string): void { this.controller?.updateAuthToken(token); }
// Call when the active account changes updateAccount(hostData: unknown): void { this.controller?.updateHostData(hostData as never); }
ngOnDestroy(): void { this.controller?.dispose(); }}Notes:
DomSanitizer.bypassSecurityTrustResourceUrlis only needed because Angular sanitizes[src]bindings. A staticsrcattribute in the template does not require it.- Wire
onNavigatetoRouter.navigateByUrlso CDP deep links update the Angular URL. - Always
dispose()inngOnDestroyto remove message listeners.
Path B — Module Federation mount (advanced)
Section titled “Path B — Module Federation mount (advanced)”Use this only if you need CDP composited directly into the Angular DOM (no iframe). The
CDP remote is React; your Angular host loads remoteEntry.js and calls mount() into a
container element.
Load the remote
Section titled “Load the remote”With @angular-architects/native-federation
(or webpack Module Federation), register the CDP remote:
// federation.config.js (native-federation) — conceptual{ remotes: { cdpApp: "https://cdp.segmentify.com/assets/remoteEntry.js", }, shared: { react: { singleton: true, requiredVersion: "18.2.0" }, "react-dom": { singleton: true, requiredVersion: "18.2.0" }, "react-intl": { singleton: true }, "@tanstack/react-query": { singleton: true }, "react-hook-form": { singleton: true }, },}Mount into an Angular component
Section titled “Mount into an Angular component”import { AfterViewInit, Component, ElementRef, OnDestroy, ViewChild } from "@angular/core";import { Router } from "@angular/router";import { createEmbedApiClient } from "@segmentify/common";
@Component({ selector: "app-cdp-federated", template: `<div #cdpRoot></div>`,})export class CdpFederatedComponent implements AfterViewInit, OnDestroy { @ViewChild("cdpRoot", { static: true }) root!: ElementRef<HTMLDivElement>; private handle?: { unmount: () => void };
constructor( private router: Router, private auth: AuthService ) {}
async ngAfterViewInit(): Promise<void> { // Dynamic import resolved by your federation runtime const { mount } = await import("cdpApp/mount");
this.handle = await mount(this.root.nativeElement, { basename: "/app/segmentify/cdp", host: { environment: "production", hostBaseUrl: "https://sauron.segmentify.com", cdpBaseUrl: "https://api.segmentify.com", cdpApi: createEmbedApiClient({ baseURL: "https://api.segmentify.com", getAuthToken: () => this.auth.getBearerToken(), defaultHeaders: { "X-Switch-User": this.auth.getSwitchUser(), "X-Switch-Region": "ALL", "X-Api-Version": "1.0", }, }), hostData: this.auth.getHostData(), onNavigate: (path: string) => this.router.navigateByUrl(path), }, }); }
ngOnDestroy(): void { this.handle?.unmount(); }}CDP renders inside a #cdp-fr-app container and sets data-cdp-fr-app on <body>. Do
not remove that markup — CDP’s scoped styles rely on it.
CDP-specific init payload
Section titled “CDP-specific init payload”Whichever path you choose, the host init requires:
{ environment: "production", hostBaseUrl: "https://sauron.segmentify.com", cdpBaseUrl: "https://api.segmentify.com", // required for CDP authToken: bearerToken, hostData: { /* apiKey, dataCenter, mainCurrency */ }, requestHeaders: { "X-Switch-User": "…", "X-Switch-Region": "ALL" },}Which path should we use?
Section titled “Which path should we use?”- Start with the iframe. It is the lowest-risk integration and isolates React from Angular entirely.
- Only move to Module Federation if you need CDP inline in the Angular DOM (shared layout chrome, no iframe scroll/resize handling) and can maintain the shared React singleton contract.
See Troubleshooting for shared-dependency and blank-screen issues, and Security for CSP and origin requirements.