
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.

{% callout type="note" heading="Browser SDK only" %}
Cross-origin iframe recording applies to the [Session Replay Browser SDK Plugin](/docs/sdks/session-replay/session-replay-plugin) and [Session Replay Standalone SDK](/docs/sdks/session-replay/session-replay-standalone-sdk). Mobile Session Replay SDKs don't support this feature.
{% /callout %}

## How it works

When you enable cross-origin iframe recording on the parent page, the SDK:

1. Watches for `<iframe>` elements added to the DOM.
2. Sends `start` and `stop` signals to child frames over `postMessage`.
3. 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

```javascript
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()`:

```javascript
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:

```javascript
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: false`** opts 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 `crossOriginIframes` on both pages still works.

## Validate your setup

1. Confirm the parent and each child page load the Session Replay SDK with `crossOriginIframes.enabled: true`.
2. Use the same `apiKey`, `deviceId`, and `sessionId` on both pages.
3. Add or navigate to the child iframe after the parent SDK initializes.
4. 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](/docs/sdks/session-replay/session-replay-plugin#css-styling-doesnt-appear-in-a-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](https://gethelp.amplitude.com/hc/en-us/requests/new) with the session replay ID. |

## Related documentation

- [Session Replay Browser SDK Plugin](/docs/sdks/session-replay/session-replay-plugin)
- [Session Replay Standalone SDK](/docs/sdks/session-replay/session-replay-standalone-sdk)
- [Session Replay ingestion monitor](/docs/session-replay/ingestion-monitor)
