Skip to content

React Native session replay should allow wireframe mode #3796

Closed as not planned

Description

@fkreinh

Bug description

React Native session replay appears to force screenshot mode on both native platforms, with no JS config option to use mobile wireframe mode instead.

The public mobile replay docs describe wireframe mode as the default and screenshot mode as opt-in:
https://posthog.com/docs/session-replay/mobile#wireframe-mode-default

However, the current posthog-react-native-session-replay native bridge enables screenshot mode unconditionally:

That means React Native apps cannot opt into wireframe mode through sessionReplayConfig, even though wireframe mode would avoid screenshot capture costs and reduce privacy/performance risk.

This is especially painful on Android because screenshot capture has been involved in layout/safe-area glitches such as #3140. In our app, we previously had to disable Android replay entirely. We would like to re-enable Android replay, but use wireframe mode on both iOS and Android.

Expected outcome

React Native session replay should either:

  1. default to wireframe mode, matching the mobile replay docs, or
  2. expose a JS config option such as sessionReplayConfig.screenshotMode / sessionReplayConfig.screenshot / sessionReplayConfig.captureMode so apps can choose wireframe vs screenshot mode per platform.

Actual outcome

posthog-react-native-session-replay forces screenshot mode in native code, so React Native apps need a dependency patch or native fork to use wireframe mode. That requires native rebuilds and is brittle.

Related sub-libraries

  • All of them
  • posthog-js (web)
  • posthog-js-lite (web lite)
  • posthog-node
  • posthog-react-native
  • @posthog/react
  • @posthog/ai
  • @posthog/convex
  • @posthog/nextjs-config
  • @posthog/nuxt
  • @posthog/rollup-plugin
  • @posthog/webpack-plugin

Environment

  • posthog-react-native: 4.45.5
  • posthog-react-native-session-replay: 1.5.8 locally; confirmed current upstream 1.6.0 still has the same hard-coded screenshot mode
  • React Native / Expo app
  • Affected platforms: iOS and Android

Activity

  1. dustinbyrne commented on Jun 10, 2026

    @dustinbyrne
    Contributor

    Hey @fkreinh, thanks for the issue! You're right that the docs are misleading here. The "wireframe mode (default)" header applies to the native Android and iOS SDKs. React Native intentionally forces screenshot mode in the native bridge, same as Flutter (whose docs do note this, while the RN docs don't). I'll fix up the documentation to make that explicit.

    The reason it's forced is that we don't currently have a wireframe capture implementation for React Native itself. The native SDKs for Android and iOS do support wireframe mode, but they're designed around UIKit/Android view hierarchies. React Native renders via its own view subclasses that the wireframe capture doesn't currently recognize.

    Regarding #3140, are you still experiencing similar layout issues on the latest version(s)?

  2. fkreinh commented on Jun 19, 2026

    @fkreinh
    Author

    @dustinbyrne Thanks for clarifying.

    On our side, we keep PostHog session replay disabled on Android because the performance overhead was too high. On iOS, screenshot-mode replay works reasonably well, but we still see occasional OS watchdog terminations, likely from RAM pressure. Throttling and image masking made it manageable, but it remains a tradeoff.

    I’d like to see wireframe mode become a first-party React Native implementation. I patched the RN session replay lib to avoid forced screenshot mode and use the native wireframe path, but the recordings were not usable for our CS/product team, especially because React Native text was not captured/rendered correctly.

    We’re not blocked on #3140 specifically. The broader issue is that screenshot mode is expensive on image-heavy/high-activity screens, while React Native wireframe mode is not production-ready today.

    Screenshots from the patched wireframe attempt:

    Image Image Image
  3. fkreinh commented on Jun 19, 2026

    @fkreinh
    Author

    One follow-up: I’m trying to downscale iOS replay screenshots from native device scale to 1x.

    For our base, that’s usually 3x -> 1x on iPhone 14+ devices, which should make screenshot-mode replay lighter. Today I have to patch the generated Podfile with an Expo config plugin to rewrite the PostHog iOS pod after install.

    It would be great if this were a first-party RN replay prop, e.g. sessionReplayConfig.screenshotScale, so apps can trade sharpness for lower memory/perf cost.

    Relevant iOS code:
    https://github.com/PostHog/posthog-ios/blob/6fa078ec4f9439389ad9f2e05692774691c3dbd7/PostHog/Replay/UIView%2BUtil.swift#L58

    Hacky plugin code withPostHogReplayScreenshotScale.js

    const { withPodfile } = require("@expo/config-plugins")
    
    const patchFunctionName = "patch_posthog_replay_screenshot_scale"
    const patchFunctionCall = `    ${patchFunctionName}(installer)`
    const patchFunctionDefinition = `def ${patchFunctionName}(installer)`
    
    const patchFunction = `
    ${patchFunctionDefinition}
      posthog_util_file = File.join(
        installer.sandbox.root.to_s,
        "PostHog/PostHog/Replay/UIView+Util.swift",
      )
    
      return unless File.exist?(posthog_util_file)
    
      contents = File.read(posthog_util_file)
      marker = "let renderer = PostHogGraphicsImageRenderer(size: size, scale: replayScreenshotScale)"
    
      return if contents.include?(marker)
    
      replacement = <<-'SWIFT'
                // Handoff patch: capture replay screenshots at 1x to reduce iOS
                // memory pressure. PostHog defaults to device scale, which is
                // usually 3x for our iPhone 14+ user base.
                let replayScreenshotScale: CGFloat = 1
                let renderer = PostHogGraphicsImageRenderer(size: size, scale: replayScreenshotScale)
      SWIFT
    
      pattern = /^\\s*let nativeScale = .*\\n\\s*let renderer = PostHogGraphicsImageRenderer\\(size: size, scale: nativeScale\\)$/
    
      unless contents.match?(pattern)
        raise "Could not patch PostHog replay screenshot scale; UIView+Util.swift changed upstream."
      end
    
      File.write(posthog_util_file, contents.sub(pattern, replacement))
    end
    `
    
    function addPatchFunction(contents) {
        if (contents.includes(patchFunctionDefinition)) {
            return contents
        }
    
        return contents.replace(
            /(\nENV\['EX_DEV_CLIENT_NETWORK_INSPECTOR'\])/,
            `${patchFunction}\n$1`,
        )
    }
    
    function addPatchCall(contents) {
        if (contents.includes(`\n${patchFunctionCall}`)) {
            return contents
        }
    
        return contents.replace(
            /(\n\s+react_native_post_install\([\s\S]*?\n\s+\))/,
            `$1\n${patchFunctionCall}`,
        )
    }
    
    function patchPodfile(contents) {
        let updatedContents = addPatchFunction(contents)
        updatedContents = addPatchCall(updatedContents)
    
        if (!updatedContents.includes(patchFunctionDefinition)) {
            throw new Error("Could not add PostHog replay screenshot scale patch")
        }
    
        if (!updatedContents.includes(`\n${patchFunctionCall}`)) {
            throw new Error("Could not call PostHog replay screenshot scale patch")
        }
    
        return updatedContents
    }
    
    module.exports = function withPostHogReplayScreenshotScale(config) {
        return withPodfile(config, (projectConfig) => {
            projectConfig.modResults.contents = patchPodfile(
                projectConfig.modResults.contents,
            )
    
            return projectConfig
        })
    }
    
    module.exports.patchPodfile = patchPodfile
  4. github-actions commented on Jul 7, 2026

    @github-actions
    Contributor

    AI triage for #3796

    Conclusion: Valid React Native session replay feature request; RN currently forces screenshot mode and has no supported wireframe mode option.
    Confidence: 93%
    Suggested labels: enhancement, feature/replay, react-native, feature/mobile, iOS, Android

    @PostHog/team-client-libraries please review this sweep triage.

    Confirmed from the current repo that React Native replay still has no public capture-mode option and the native bridge forces screenshot mode on both platforms.

    A small JS config passthrough would be mechanically possible, but based on the maintainer clarification and the reporter's patched experiment, simply setting the native SDK to wireframe would expose an unsupported/poor-quality RN recording path rather than a production-ready RN wireframe implementation. This should stay tracked as a mobile replay enhancement, with a separate docs clarification if not already handled.

    Investigation

    • packages/react-native/src/types.ts defines PostHogSessionReplayConfig with masking, logs, network telemetry, throttle/debouncer, screenshotModeBackgroundCapture, and sampleRate, but no screenshot, screenshotMode, captureMode, or screenshotScale option.
    • packages/react-native/src/posthog-rn.ts builds sdkReplayConfig from the exposed JS config and forwards it to the optional native plugin, but it does not include any capture-mode or screenshot-scale setting.
    • packages/react-native-plugin/ios/PosthogReactNativePlugin.swift unconditionally sets config.sessionReplayConfig.screenshotMode = true before applying the rest of the replay config.
    • packages/react-native-plugin/android/src/main/java/com/posthogreactnativeplugin/PosthogReactNativePluginModule.kt unconditionally sets sessionReplayConfig.screenshot = true before applying log/throttle/masking/sample-rate settings.
    • packages/react-native/src/optional/OptionalPlugin.ts resolves @posthog/react-native-plugin first and falls back to posthog-react-native-session-replay, so this native bridge path is the relevant integration path for RN replay.
    • packages/react-native-plugin/CHANGELOG.md for fix(react-native-plugin): drop duplicate native captures of fatal JS errors and apply replay config at setup #3824 explicitly says replay config is applied so later recording uses screenshot mode instead of wireframe/default settings, which supports the maintainer comment that screenshot mode is intentional today.
    • Searches found no RN-specific wireframe capture implementation in the React Native package/plugin; the plugin delegates to native iOS/Android SDK dependencies declared in the podspec and Gradle file.

    Relevant files: packages/react-native/src/types.ts, packages/react-native/src/posthog-rn.ts, packages/react-native/src/optional/OptionalPlugin.ts, packages/react-native-plugin/ios/PosthogReactNativePlugin.swift, packages/react-native-plugin/android/src/main/java/com/posthogreactnativeplugin/PosthogReactNativePluginModule.kt, packages/react-native-plugin/posthog-react-native-plugin.podspec, packages/react-native-plugin/android/build.gradle, packages/react-native-plugin/CHANGELOG.md

    Fix assessment

    • Straightforward: no
    • Risk: high
    • Reason: Changing the default to wireframe would be a behavior change and, per maintainer clarification and the reporter's patched test, the native SDK wireframe path does not produce usable React Native recordings today. Exposing a raw screenshotMode=false switch is small but would likely create an unsupported/broken capture mode rather than solving the feature request. A production fix needs RN-specific wireframe capture semantics and cross-platform native work.

    Fix PR skipped: feature requests require an explicit trusted fix command or mode: fix before opening a draft PR

    Pi session

    The JSONL pi session file(s) for this run were saved to the posthog-watcher-state branch. Download one locally, then fork it into your own pi session:

    pi --fork path/to/session.jsonl

    Saved session files:

    Close proposal

    None. No issue close action was taken.


    Generated by posthog-watcher-action using pi.

  5. 4shutosh commented on Sep 2, 2026

    @4shutosh

    Forced screenshot mode is blocking us from safely keeping Android replay enabled.

    We see repeated production OOMs across several users and devices, mostly on media-heavy screens. The screenshot allocation path matches the memory pressure described in PostHog/posthog-android#750.

    A supported lower-memory capture mode or screenshot scale would help us keep replay enabled.

  6. marandaneto commented on Sep 5, 2026

    @marandaneto
    Member

    we'll deprecate wireframe mode in favor of snapshots only
    the perf issues in snapshot mode will be addressed separately

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions