Repository navigation
Conversation
On iOS a React Native `Modal` presents from the root view controller's presentation chain. When a native-stack screen is already presented (e.g. `presentation: 'formSheet'`), UIKit refuses the second presentation and the feedback form silently fails to appear (#6875). Render the form inside react-native-screens' `FullWindowOverlay` on iOS when it resolves (optional require, Metro-optional dependency), falling back to `Modal` otherwise. Android stays on `Modal`. This covers both `showFeedbackForm()` and shake-to-report, since both go through `FeedbackFormProvider`. WIP: currently auto-enabled whenever react-native-screens is present. Still TODO: opt-in experimental option (`_experiments.presentOverNativeModals`), overlay-path unit tests, device-verify close/swipe-dismiss, CHANGELOG. Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…presentOverNativeModals
Make the FullWindowOverlay presentation opt-in instead of auto-enabled:
`feedbackIntegration({ _experiments: { presentOverNativeModals: true } })`
(iOS only, requires react-native-screens; default off). When disabled or
when react-native-screens isn't installed, the form keeps using Modal; an
enabled-but-missing setup logs a debug warning.
- integration: add `_experiments.presentOverNativeModals` + exported
`FeedbackExperiments` type and `isPresentOverNativeModalsEnabled()`.
- provider: gate the overlay branch on the option; warn on fallback.
- tests: default Modal / overlay-when-enabled / fallback-and-warn.
- api-report + CHANGELOG updated.
Verified on iOS sim (New Arch): with the option on, Sentry.showFeedbackForm()
renders above a native-stack formSheet with no "already presenting" error.
Still TODO: Old Architecture verification, device-verify close/swipe-dismiss
and interactive keyboard, decide whether to default-on in a later release.
Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add an "Open Form Sheet" entry to the Playground tab that presents a native-stack formSheet with a button to open the feedback form, demonstrating `_experiments.presentOverNativeModals`. - Remove issue-number references from code comments and test names. Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
🤖 This preview updates automatically when you update the PR. |
Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rlay
Address review feedback on the experimental `presentOverNativeModals` option:
- Re-render the form on `Dimensions` change so the FullWindowOverlay stays
full-screen after rotation/resize (it was sized once from
`Dimensions.get('window')` at render).
- Document that the photo-library screenshot picker ("Add a screenshot") is
presented behind the overlay and can't be used while the form is open;
native "Take a screenshot", alerts, and text entry are unaffected.
Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit edc646d. Configure here.
📲 Install BuildsAndroid
|
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 038a6d7+dirty | 3849.69 ms | 1228.40 ms | -2621.28 ms |
| d771cd5+dirty | 3839.40 ms | 1211.08 ms | -2628.32 ms |
| 41d6254+dirty | 3845.71 ms | 1224.51 ms | -2621.20 ms |
| a858ac3+dirty | 3836.62 ms | 1222.56 ms | -2614.06 ms |
| 20fbd51+dirty | 3827.47 ms | 1213.32 ms | -2614.15 ms |
| 5920f6b+dirty | 3866.23 ms | 1239.47 ms | -2626.76 ms |
| 5748023+dirty | 3840.49 ms | 1227.43 ms | -2613.05 ms |
| 9ad4522+dirty | 3850.40 ms | 1222.40 ms | -2628.00 ms |
| d0e3b3e+dirty | 3854.77 ms | 1218.58 ms | -2636.19 ms |
| 3b6e9f9+dirty | 3851.90 ms | 1233.33 ms | -2618.57 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 038a6d7+dirty | 5.15 MiB | 6.70 MiB | 1.55 MiB |
| d771cd5+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| 41d6254+dirty | 5.15 MiB | 6.70 MiB | 1.54 MiB |
| a858ac3+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 20fbd51+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 5920f6b+dirty | 5.15 MiB | 6.93 MiB | 1.78 MiB |
| 5748023+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 9ad4522+dirty | 4.98 MiB | 6.56 MiB | 1.58 MiB |
| d0e3b3e+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 3b6e9f9+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 23598c3+dirty | 414.12 ms | 426.24 ms | 12.12 ms |
| bc8f61e+dirty | 442.98 ms | 506.56 ms | 63.58 ms |
| 0a9e622+dirty | 420.73 ms | 464.83 ms | 44.10 ms |
| bf168a4+dirty | 418.21 ms | 489.74 ms | 71.53 ms |
| b0d3373+dirty | 557.66 ms | 579.42 ms | 21.76 ms |
| be120d6+dirty | 536.61 ms | 552.02 ms | 15.41 ms |
| 57e0069+dirty | 413.82 ms | 453.47 ms | 39.65 ms |
| b9bebee+dirty | 438.86 ms | 452.21 ms | 13.35 ms |
| 0d9949d+dirty | 403.57 ms | 437.00 ms | 33.43 ms |
| 580fb5c+dirty | 436.34 ms | 471.63 ms | 35.28 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 23598c3+dirty | 43.75 MiB | 48.16 MiB | 4.41 MiB |
| bc8f61e+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 0a9e622+dirty | 49.74 MiB | 55.09 MiB | 5.34 MiB |
| bf168a4+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| b0d3373+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| be120d6+dirty | 49.74 MiB | 55.36 MiB | 5.62 MiB |
| 57e0069+dirty | 49.74 MiB | 54.85 MiB | 5.11 MiB |
| b9bebee+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| 0d9949d+dirty | 43.75 MiB | 48.13 MiB | 4.37 MiB |
| 580fb5c+dirty | 49.74 MiB | 54.79 MiB | 5.05 MiB |
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 038a6d7+dirty | 499.02 ms | 527.68 ms | 28.66 ms |
| 27d9693+dirty | 438.63 ms | 514.08 ms | 75.46 ms |
| 3d377b5+dirty | 425.38 ms | 440.67 ms | 15.30 ms |
| 0668149+dirty | 410.52 ms | 454.12 ms | 43.60 ms |
| 580fb5c+dirty | 515.72 ms | 585.38 ms | 69.66 ms |
| 0bd8916+dirty | 400.15 ms | 442.72 ms | 42.57 ms |
| 44c8b3f+dirty | 492.13 ms | 563.47 ms | 71.34 ms |
| 822d35b+dirty | 472.69 ms | 561.24 ms | 88.56 ms |
| 7d6fd3a+dirty | 435.06 ms | 458.78 ms | 23.72 ms |
| 7ff4d0f+dirty | 403.38 ms | 427.06 ms | 23.68 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 038a6d7+dirty | 48.30 MiB | 53.60 MiB | 5.30 MiB |
| 27d9693+dirty | 49.74 MiB | 55.09 MiB | 5.34 MiB |
| 3d377b5+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
| 0668149+dirty | 50.56 MiB | 56.59 MiB | 6.03 MiB |
| 580fb5c+dirty | 49.74 MiB | 54.79 MiB | 5.05 MiB |
| 0bd8916+dirty | 48.30 MiB | 53.57 MiB | 5.26 MiB |
| 44c8b3f+dirty | 48.30 MiB | 53.46 MiB | 5.15 MiB |
| 822d35b+dirty | 49.74 MiB | 54.84 MiB | 5.10 MiB |
| 7d6fd3a+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
| 7ff4d0f+dirty | 48.30 MiB | 53.60 MiB | 5.30 MiB |
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 1122a96+dirty | 3839.17 ms | 1219.23 ms | -2619.93 ms |
| 9210ae6+dirty | 3834.11 ms | 1216.64 ms | -2617.47 ms |
| fa21fca+dirty | 3845.12 ms | 1215.89 ms | -2629.24 ms |
| 5a23c47+dirty | 3843.54 ms | 1226.05 ms | -2617.50 ms |
| 3ce5254+dirty | 1217.70 ms | 1224.69 ms | 6.99 ms |
| 9474ead+dirty | 3823.33 ms | 1208.31 ms | -2615.03 ms |
| a2585ce+dirty | 3837.98 ms | 1214.20 ms | -2623.77 ms |
| 7c6200e+dirty | 3828.16 ms | 1217.20 ms | -2610.95 ms |
| 04207c4+dirty | 1228.55 ms | 1226.04 ms | -2.51 ms |
| 606fb09+dirty | 3850.45 ms | 1229.81 ms | -2620.63 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 1122a96+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 9210ae6+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| fa21fca+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| 5a23c47+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 9474ead+dirty | 5.15 MiB | 6.71 MiB | 1.55 MiB |
| a2585ce+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| 7c6200e+dirty | 5.08 MiB | 6.68 MiB | 1.60 MiB |
| 04207c4+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 606fb09+dirty | 5.15 MiB | 6.95 MiB | 1.80 MiB |
…rlay docs
Address review feedback on the experimental `presentOverNativeModals` option:
- Declare `react-native-screens` as an optional peer dependency (matching the
existing `@react-navigation/native` pattern) so the optional `require` resolves
under strict / Yarn PnP resolvers instead of silently falling back to `Modal`.
- Extend the documented limitation: over a presented native-stack sheet, both
screenshot attachment flows are limited — the library picker ("Add a
screenshot") is behind the overlay, and "Take a screenshot" reveals its capture
button in the root tree behind the sheet.
Signed-off-by: Antonis Lilis <antonis.lilis@sentry.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| enableScreenshot: true, | ||
| enableTakeScreenshot: true, | ||
| enableShakeToReport: true, | ||
| _experiments: { presentOverNativeModals: true }, |
There was a problem hiding this comment.
what I find wrong is that this setting turns on presentOverNativeModals for the whole sample app.
The Maestro e2e tests run against this sample in sample-application.yml which means that after this change iOS CI only tests the experimental overlay path.
There was a problem hiding this comment.
Good point 👍 Given that this served the purpose of testing we can revert this change
| _experiments: { presentOverNativeModals: true }, |
Size Analysis2 components analyzed iOS Builds
Android Builds
|
| onPress={() => { | ||
| props.navigation.navigate('FormSheet'); | ||
| }} | ||
| /> |
There was a problem hiding this comment.
see warning below:
[TAV-U7K] Form-sheet sample does not enable the overlay experiment (additional location)
Check warning: [TAV-U7K] Form-sheet sample does not enable the overlay experiment (additional location)
The new FormSheetScreen says the feedback form appears above the sheet, but the sample's feedbackIntegration() does not set _experiments.presentOverNativeModals. The option therefore defaults to false; on iOS the provider uses the Modal path, which cannot present the form over an already-presented native-stack sheet. Enable the experiment in the sample integration or clarify that the demo requires manual configuration.
| import * as Sentry from '@sentry/react-native'; | ||
| import { isTurboModuleEnabled } from '@sentry/react-native/dist/js/utils/environment'; | ||
| import { LogBox, Platform } from 'react-native'; | ||
| import { Button, LogBox, Platform, Text, View } from 'react-native'; |
There was a problem hiding this comment.
see warning below
Form-sheet sample does not enable the overlay experiment
Check warning: Form-sheet sample does not enable the overlay experiment
The new FormSheetScreen says the feedback form appears above the sheet, but the sample's feedbackIntegration() does not set _experiments.presentOverNativeModals. The option therefore defaults to false; on iOS the provider uses the Modal path, which cannot present the form over an already-presented native-stack sheet. Enable the experiment in the sample integration or clarify that the demo requires manual configuration.
| "react": ">=17.0.0", | ||
| "react-native": ">=0.65.0" | ||
| "react-native": ">=0.65.0", | ||
| "react-native-screens": "*" |
There was a problem hiding this comment.
Might be worth to set a minimum, old versions may have issues with our integration
lucas-zimerman
left a comment
There was a problem hiding this comment.
minor nits found by ci, other than that, LGTM!
📢 Type of change
📜 Description
This adds an opt-in, experimental option so the form is rendered inside react-native-screens'
FullWindowOverlay(window-level, above presented sheets) instead of aModal:react-native-screens— no-ops (falls back toModal, with adebug.warn) when it isn't installed.showFeedbackForm()and shake-to-report.Modalpath is unchanged when the option is off.Known limitation: with the overlay on, the screenshot attachment flows are limited over a presented native-stack sheet — the photo-library picker ("Add a screenshot") is presented behind the overlay, and "Take a screenshot" reveals its capture button in the root tree behind the sheet. Alerts (validation/success), typing, Cancel and swipe-to-dismiss are unaffected. This is documented in the option's JSDoc.
💡 Motivation and Context
Fixes #6875.
💚 How did you test it?
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2026-10-13.at.13.43.37.mov
📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps