Cross-origin iframe recording
The Session Replay Browser SDK can capture DOM changes inside cross-origin <iframe> elements and merge them into the parent page's replay stream. Both the parent page and each child iframe page must load the SDK with crossOriginIframes.enabled: true.
This article covers configuration, privacy behavior, limitations, and troubleshooting.
Browser SDK only
Cross-origin iframe recording applies to the Session Replay Browser SDK Plugin and Session Replay Standalone SDK. Mobile Session Replay SDKs don't support this feature.
How it works
When you enable cross-origin iframe recording on the parent page, the SDK:
- Watches for
<iframe>elements added to the DOM. - Sends
startandstopsignals to child frames overpostMessage. - Relays child rrweb events into the parent replay stream so the replay viewer can reconstruct both frames from a single session.
The child SDK detects when it runs inside an iframe and waits for a start signal from the parent before it begins recording.
Setup
Load the Session Replay SDK on both the parent page and each child iframe page you want to capture.
Parent page
import * as sessionReplay from "@amplitude/session-replay-browser";
sessionReplay.init("API_KEY", {
deviceId: "DEVICE_ID",
sessionId: SESSION_ID,
sampleRate: 1,
crossOriginIframes: {
enabled: true,
coordinateChildren: true,
},
});
If you use the Browser SDK plugin, pass the same options to sessionReplay.plugin():
import * as amplitude from "@amplitude/analytics-browser";
import { sessionReplayPlugin } from "@amplitude/plugin-session-replay-browser";
const replay = sessionReplayPlugin({
crossOriginIframes: {
enabled: true,
coordinateChildren: true,
},
});
amplitude.add(replay);
amplitude.init("API_KEY", { deviceId: "DEVICE_ID" });
Child iframe page
Initialize the SDK with the same apiKey, deviceId, and sessionId as the parent. Enable cross-origin iframes, but don't set coordinateChildren on the child:
import * as sessionReplay from "@amplitude/session-replay-browser";
sessionReplay.init("API_KEY", {
deviceId: "DEVICE_ID",
sessionId: SESSION_ID,
sampleRate: 1,
crossOriginIframes: { enabled: true },
});
Pass sessionId and deviceId to the child through the iframe URL query string when you set the iframe src.
Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
crossOriginIframes.enabled | boolean | false | Enables cross-origin iframe recording on the page. Set to true on both parent and child pages. |
crossOriginIframes.coordinateChildren | boolean | true | When true, the parent SDK sends start/stop signals to child iframes and keeps their recording lifecycle in sync. Set to false to manage child recording yourself. Applies to the parent page only. |
Privacy
The child page's rrweb instance performs its own DOM serialization. The parent's privacy config (mask levels, block selectors, and so on) doesn't automatically apply inside the iframe. Configure privacy settings independently on each child page.
Limitations
- Third-party iframes (for example, Stripe, Google Maps, or YouTube) can't be captured. You control only pages where you install the Session Replay SDK.
coordinateChildren: falseopts out of the parent coordinator. In this mode, the child SDK doesn't start recording until you manage its lifecycle directly.- Same-origin iframes don't require cross-origin configuration, but enabling
crossOriginIframeson both pages still works.
Validate your setup
- Confirm the parent and each child page load the Session Replay SDK with
crossOriginIframes.enabled: true. - Use the same
apiKey,deviceId, andsessionIdon both pages. - Add or navigate to the child iframe after the parent SDK initializes.
- Interact inside the iframe, then open the session in Amplitude's replay viewer.
For local cross-origin testing, serve the parent and child from different origins (different ports or hostnames). A public HTTPS parent can't embed an http://localhost child because of mixed content and browser Local Network Access restrictions.
Troubleshooting
The iframe is blank in the live page
| Symptom | Likely cause |
|---|---|
| Blank iframe from a public parent | Child URL uses http://localhost or another blocked origin. Serve the child over public HTTPS. |
| "Refused to connect" | Child URL is wrong or unreachable. Verify the iframe src and that the child origin allows embedding. |
| Placeholder only | Parent didn't inject an iframe yet. Add the iframe in your app after the SDK initializes. |
| Empty iframe in replay | Child page doesn't load Session Replay (for example, a static demo page). Install the SDK on the child with crossOriginIframes.enabled: true. |
The iframe is blank in the replay viewer
| Symptom | Likely cause |
|---|---|
| Short session looks empty | The first child merge may arrive ~1 second after iframe injection. Record a longer session and interact inside the iframe. |
| Missing styles or low contrast | CSS didn't replay correctly — not missing DOM. Check external stylesheets, add crossorigin="anonymous" on <link rel="stylesheet">, and allow app.amplitude.com in CORS. Go to CSS styling doesn't appear in replay. |
| iframe shell with no content | Child DOM merged via incrementals but a later checkout snapshot may omit iframe children. If capture looks correct in raw events but replay shows an empty frame, contact Amplitude support with the session replay ID. |
Related documentation
Was this helpful?