Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/calm-tabs-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'react-native-bottom-tabs': patch
---

Prevent a white flash when opening a lazy tab for the first time on iOS 27.
28 changes: 25 additions & 3 deletions apps/example/src/Examples/NativeBottomTabsLazy.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Article } from '../Screens/Article';
import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
import { StyleSheet, Text, View } from 'react-native';

const Tab = createNativeBottomTabNavigator();

function DarkScreen() {
return (
<View testID="lazyDarkScreen" style={styles.darkScreen}>
<Text style={styles.darkScreenText}>Lazy screen with dark content</Text>
</View>
);
}

export default function NativeBottomTabsLazy() {
return (
<Tab.Navigator>
Expand All @@ -32,12 +40,26 @@ export default function NativeBottomTabsLazy() {
}}
/>
<Tab.Screen
name="Chat"
component={Chat}
name="Dark"
component={DarkScreen}
options={{
tabBarButtonTestID: 'lazyDarkTab',
tabBarIcon: () => require('../../assets/icons/chat_dark.png'),
}}
/>
</Tab.Navigator>
);
}

const styles = StyleSheet.create({
darkScreen: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#1E2D2F',
},
darkScreenText: {
color: '#F7DBA7',
fontSize: 24,
},
});
38 changes: 38 additions & 0 deletions artifacts/issue-547/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Issue #547 verification evidence

This directory records the reproduction and verification for the iOS 27 lazy-tab white flash.

## Environment

- Base commit: `1bbcaa2`
- Xcode: 27.0 (`27A5228h`)
- iOS 27 simulator runtime: 27.0 (`24A5390f`)
- iOS 27 device: iPhone 17 Pro (`CD1ADD09-474F-4618-899B-689243327BC6`)
- Compatibility runtime: iOS 26.5 (`23F77`)
- iOS 26 device: iPhone 17 Pro (`951CC10B-1B07-48EE-8636-59386FD2C58C`)
- Example app: React Native 0.81.4, Hermes, New Architecture
- `agent-device`: 0.20.10

## Reproduction

The current example app's **Native > Lazy Tabs** screen was launched with the navigator's default `lazy: true`. Starting on Article, Contacts was selected for the first time while recording at 50 fps. The native tab switched before React had mounted the lazy scene, producing a fully white content frame between the two screens.

- [`before/videos/first-lazy-contacts-review.mp4`](before/videos/first-lazy-contacts-review.mp4): three-second review clip
- [`before/screenshots/transition-contact-sheet.png`](before/screenshots/transition-contact-sheet.png): sampled transition frames
- [`before/screenshots/white-flash-frame.png`](before/screenshots/white-flash-frame.png): captured white frame
- [`before/logs/app.log`](before/logs/app.log): application log for the run

The untrimmed 50 fps capture and gesture telemetry are retained in `before/videos/`.

## Verification after the fix

The identical first activation was recorded at 75 fps. Article remains visible until the mounted Contacts scene is ready; no empty frame appears. A second run activates a lazy screen with a permanent `#1E2D2F` background, confirming that the implementation does not depend on masking the transition with the default color.

- [`after/videos/first-lazy-contacts.mp4`](after/videos/first-lazy-contacts.mp4): default appearance, 3.29 seconds at 75 fps
- [`after/screenshots/contacts-transition-contact-sheet.png`](after/screenshots/contacts-transition-contact-sheet.png): sampled default transition frames
- [`after/videos/first-lazy-dark.mp4`](after/videos/first-lazy-dark.mp4): non-white appearance, 3.67 seconds at 75 fps
- [`after/screenshots/dark-transition-contact-sheet.png`](after/screenshots/dark-transition-contact-sheet.png): sampled dark transition frames
- [`after/logs/app.log`](after/logs/app.log): application log for both fixed runs
- [`after/logs/xcodebuild-ios27.log`](after/logs/xcodebuild-ios27.log): successful iOS 27 simulator build

For each fixed run, accessibility state was explicitly queried after the interaction: the first Contacts row was `M, Marissa Castillo, 7766398169`, and the dark screen exposed `Lazy screen with dark content`. The same first-load Contacts path was also exercised on iOS 26.5; [`after/ios26/contacts-loaded.png`](after/ios26/contacts-loaded.png) records the resulting state.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4,350 changes: 4,350 additions & 0 deletions artifacts/issue-547/after/logs/app.log

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions artifacts/issue-547/after/logs/launch-console.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
2026-08-27 01:17:56.587 ReactTestApp[1584:141786592] You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
2026-08-27 01:17:56.966 ReactTestApp[1584:141786592] Unbalanced calls to begin/end appearance transitions for <UIViewController: 0x10a06f480>.
objc[1584]: Class UIAccessibilityLoaderWebShared is implemented in both /private/var/run/com.apple.security.cryptexd/mnt/com.apple.iPhoneOS.SimulatorRuntime-v24.1.5390.6.rDKeNs/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 27.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/AccessibilityBundles/WebCore.axbundle/WebCore (0x1125dc310) and /private/var/run/com.apple.security.cryptexd/mnt/com.apple.iPhoneOS.SimulatorRuntime-v24.1.5390.6.rDKeNs/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 27.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/AccessibilityBundles/WebKit.axbundle/WebKit (0x111f94398). This may cause spurious casting failures and mysterious crashes. One of the duplicates must be removed or renamed.
Loading