feat(preview): capture sandbox preview iframe in PostHog session replay - #5203
Open
aka-sacci-ccr wants to merge 1 commit into
Open
feat(preview): capture sandbox preview iframe in PostHog session replay#5203aka-sacci-ccr wants to merge 1 commit into
aka-sacci-ccr wants to merge 1 commit into
Conversation
The Preview iframe loads a deco site on a different origin (*.preview-studio.decocms.com), and rrweb only records same-origin iframes — so the preview canvas showed up as a blank rectangle in replays. Enable PostHog cross-origin iframe recording: - set `recordCrossOriginIframes: true` on the parent's session_recording - inject a lean posthog-js bootstrap into the sandbox preview that inits with the same flag, so the child forwards its rrweb stream to the parent instead of ingesting on its own (one stitched recording) The child bootstrap is injected only via the sandbox's existing `visual-editor::activate` channel (present only in sandbox dev-server previews, never the production fallback), and after the `display.mode === "sandbox"` guard — so recording is scoped to deco sites the user is actively editing in Preview. It disables autocapture/pageviews and mirrors the parent's input masking, so it contributes only the replay stream.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PostHog session replay was showing the Preview canvas as a blank rectangle. The preview
<iframe>loads a deco site on a different origin (*.preview-studio.decocms.com) than the app (studio.decocms.com), and rrweb only records same-origin iframes by default.This enables PostHog's cross-origin iframe recording so the deco site being previewed is stitched into the parent's recording:
lib/posthog-client.ts): setsession_recording.recordCrossOriginIframes: true.components/sandbox/preview/session-replay-script.ts, new): a lean posthog-js bootstrap injected into the preview iframe, inited with the same flag. The child detects it's embedded and forwards its rrweb stream to the parent via postMessage instead of ingesting on its own → one stitched recording.preview.tsx): on iframeonLoad, after thedisplay.mode === "sandbox"guard, via the sandbox's existingvisual-editor::activatechannel (new Function(script)()inIFRAME_BOOTSTRAP_SCRIPT).Scoping — only Preview-tab iframes (deco sites)
The
visual-editor::activatebootstrap is injected by the daemon proxy only into sandbox dev-server previews, never the production-fallback frame; combined with thedisplay.mode === "sandbox"guard, recording is scoped to deco sites the user is actively editing. The child disables autocapture/pageviews/pageleaves and mirrors the parent's input masking (maskAllInputs,blockClass: "ph-no-capture"), so it contributes only the replay stream — no duplicate analytics events from inside the customer site.Testing notes
Local dev already serves the preview cross-origin (
<handle>.localhost), so the path is reproducible, but this PR is meant to be validated on the preview env:window.__phSessionReplayActive === trueandwindow.posthogexists; Network shows/static/array.js.posthog.config.session_recording.recordCrossOriginIframes === true; openposthog.get_session_replay_url().Session replay is gated by project-level sampling (10%) + min duration (10s); the child inherits the parent's session, so no separate config.
The child loads posthog-js from
${POSTHOG_HOST}/static/array.js(the same first-party reverse proxy the parent uses). That proxy must serve/static/*(standard PostHog reverse-proxy contract). Ifarray.js404s inside the iframe, the recorder won't load — please confirm on the preview env.Checklist
bun run fmt/fmt:checkbun run check(tsc)bun run lintSummary by cubic
Enable cross-origin session replay for the Preview iframe so deco sites render inside PostHog recordings instead of a blank box. The parent enables cross-origin capture and the sandbox iframe injects a minimal
posthog-jsbootstrap to forward rrweb events to the parent.New Features
session_recording.recordCrossOriginIframes: trueand keep input masking.buildSessionReplayScript()in the preview iframe usingposthog-js; disables autocapture/pageviews and mirrors masking; forwards rrweb to the parent viapostMessagefor one stitched recording.preview.tsxvia the existingvisual-editor::activatechannel, gated bydisplay.mode === "sandbox"and project config.Migration
/static/array.js(standard PostHog reverse-proxy path) so the iframe can loadposthog-js.Written for commit 28bd8fd. Summary will update on new commits.