Commit ee27c28
fix: make in-app webview back button tappable on iOS (#29693)
## **Description**
After the React Navigation v5 → v6 migration (#26691) and the follow-up
iOS overlap fix (#29020), the back arrow in the in-app `SimpleWebview`
(used e.g. when tapping "View on block explorer" after a swap) renders
behind the iOS status bar / Dynamic Island and is unclickable, leaving
the user trapped on the screen.
Root cause:
- `SimpleWebview` was injecting its header through
`navigation.setOptions({ header: () => <HeaderCompactStandard …/> })`.
- In `@react-navigation/stack` v6, custom `header` functions do **not**
receive automatic `paddingTop: headerStatusBarHeight` (see
`node_modules/@react-navigation/stack/src/views/Header/HeaderContainer.tsx`);
the custom header must own its safe-area inset.
- The previous iOS overlap fix worked around that by setting
`includesTopInset: Device.isAndroid()`, which on iOS resulted in **no
top safe-area inset at all**, so the back button rendered at `y = 0`
underneath the status bar.
Solution (matches the pattern already used by `WebviewModal.tsx` in
Deposit):
- Render `HeaderCompactStandard` directly inside `SimpleWebview` with
`includesTopInset` always on, so `useSafeAreaInsets()` (already used by
`HeaderBase`) drives the top padding consistently on both platforms.
- Hide the inner `Stack.Navigator` headers in both webview wrappers
(`MainNavigator.js` and `App.tsx`) via `screenOptions={{ headerShown:
false }}` so we don't double-render headers.
- Remove the brittle `setOptions` custom-header wiring; this also
removes the need for the `Device.isAndroid()` platform branch and the
`rounded-t-2xl` styling that was masking the issue.
The navigation contract used by callers
(`navigation.navigate(Routes.WEBVIEW.MAIN, { screen:
Routes.WEBVIEW.SIMPLE, params: { url } })`) is unchanged, so
`BridgeTransactionDetails`, `BlockExplorersModal`, and other consumers
keep working as-is.
Longer-term, the team has discussed routing all external links through a
single service that selects between WebView and `inAppReborn` (Pedro /
Joao in the originating Slack thread); that refactor is intentionally
out of scope here — this PR just unblocks the user.
## **Changelog**
CHANGELOG entry: Fixed an iOS bug where the back button in the in-app
webview (e.g. "View on block explorer") was rendered behind the status
bar and could not be tapped.
## **Related issues**
Fixes: No issue — bug surfaced in the `#metamask-core-mobile-ux` Slack
thread on 2026-04-28 (browser back button unclickable after swap → "View
on block explorer"). Andre Alves asked for a board entry but a Jira
ticket had not been created at the time this fix was opened.
## **Manual testing steps**
\`\`\`gherkin
Feature: In-app webview back button is reachable on iOS
Scenario: User exits the in-app webview opened from a swap transaction
Given the user has completed a swap on iOS
And the user is on the swap transaction details screen
When the user taps "View on block explorer"
Then the in-app webview opens with the block explorer page
And the header is rendered fully below the status bar / Dynamic Island
And the back arrow in the top-left of the header is tappable
When the user taps the back arrow
Then the user returns to the previous screen
Scenario: Header still renders correctly on Android
Given the user is on Android
When the user opens any flow that pushes Routes.WEBVIEW.MAIN /
Routes.WEBVIEW.SIMPLE
Then the header is rendered with the correct top safe-area inset
And the share button on the right works
And the back button on the left works
Scenario: Header still renders correctly when the webview is presented
as a modal during onboarding
Given the user is in the onboarding flow on iOS
When a screen pushes Routes.WEBVIEW.MAIN (modal presentation)
Then the in-app webview opens with the header below the status bar
And the back arrow is tappable and returns to the onboarding screen
\`\`\`
## **Screenshots/Recordings**
https://github.com/user-attachments/assets/78c9e271-6e58-4b9f-862a-b6b123df2915
### **Before**
iOS: back arrow rendered behind the status bar / Dynamic Island and not
tappable — user is trapped on the webview screen (see Slack screenshot
from Heyse Li, 2026-04-28).
### **After**
iOS: header renders below the status bar with proper safe-area inset;
back arrow is fully visible and tappable.
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.
#### Performance checks (if applicable)
- [x] I've tested on Android
- Ideally on a mid-range device; emulator is acceptable
- [x] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [x] I've instrumented key operations with Sentry traces for production
performance metrics
- See [\`trace()\`](/app/util/trace.ts) for usage and
[\`addToken\`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example
For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).
## **Pre-merge reviewer checklist**
- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
Made with [Cursor](https://cursor.com)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Low risk UI/navigation change that primarily adjusts header rendering
for `SimpleWebview`; main risk is unintended header/spacing differences
or missing headers in the webview stacks.
>
> **Overview**
> Fixes an iOS layout regression where the in-app `SimpleWebview` back
button could be obscured by the status bar/Dynamic Island by **rendering
`HeaderCompactStandard` directly inside `SimpleWebview`** (always
`includesTopInset`) instead of injecting a custom header via
`navigation.setOptions()`.
>
> Updates the webview stack wrappers in `MainNavigator` and `App` to
**hide the React Navigation header** (`screenOptions={{ headerShown:
false }}`) to avoid double headers, and adjusts tests to validate the
new header rendering and callbacks (back + share).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
972444a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Andre Pimenta <andrepimenta7@gmail.com>1 parent 0b8d638 commit ee27c28
4 files changed
Lines changed: 38 additions & 76 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
318 | 318 | | |
319 | 319 | | |
320 | 320 | | |
321 | | - | |
| 321 | + | |
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
891 | 891 | | |
892 | 892 | | |
893 | 893 | | |
894 | | - | |
| 894 | + | |
895 | 895 | | |
896 | 896 | | |
897 | 897 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
| 7 | + | |
9 | 8 | | |
10 | 9 | | |
11 | | - | |
| 10 | + | |
12 | 11 | | |
13 | 12 | | |
14 | | - | |
15 | | - | |
16 | | - | |
| 13 | + | |
17 | 14 | | |
18 | 15 | | |
19 | 16 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
| 17 | + | |
28 | 18 | | |
29 | | - | |
30 | | - | |
| 19 | + | |
| 20 | + | |
31 | 21 | | |
32 | 22 | | |
33 | 23 | | |
| |||
48 | 38 | | |
49 | 39 | | |
50 | 40 | | |
51 | | - | |
| 41 | + | |
52 | 42 | | |
53 | 43 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
| 44 | + | |
61 | 45 | | |
62 | 46 | | |
63 | | - | |
64 | | - | |
| 47 | + | |
65 | 48 | | |
66 | 49 | | |
67 | 50 | | |
68 | 51 | | |
69 | 52 | | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
| 53 | + | |
87 | 54 | | |
88 | 55 | | |
89 | 56 | | |
90 | 57 | | |
91 | 58 | | |
92 | 59 | | |
93 | | - | |
| 60 | + | |
94 | 61 | | |
95 | | - | |
| 62 | + | |
96 | 63 | | |
97 | | - | |
| 64 | + | |
98 | 65 | | |
99 | 66 | | |
100 | 67 | | |
101 | 68 | | |
102 | 69 | | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
109 | 73 | | |
110 | 74 | | |
111 | 75 | | |
| |||
116 | 80 | | |
117 | 81 | | |
118 | 82 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
125 | 86 | | |
126 | 87 | | |
127 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| 3 | + | |
3 | 4 | | |
4 | | - | |
| 5 | + | |
5 | 6 | | |
| 7 | + | |
6 | 8 | | |
7 | 9 | | |
8 | 10 | | |
9 | | - | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
| 22 | + | |
20 | 23 | | |
21 | 24 | | |
22 | 25 | | |
| |||
32 | 35 | | |
33 | 36 | | |
34 | 37 | | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
48 | 49 | | |
49 | 50 | | |
50 | 51 | | |
| |||
0 commit comments