From 3d6a1acac070a8986fca81a4be33602ac637f192 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Tue, 9 Jun 2026 11:52:21 +0100 Subject: [PATCH 1/8] fix(iOS): fix screens not rendering on tab change on iOS 27 --- .gitignore | 3 ++ apps/example/src/App.tsx | 11 +++++ apps/example/src/Examples/LazyTabs.tsx | 47 +++++++++++++++++++ .../src/Examples/NativeBottomTabsLazy.tsx | 43 +++++++++++++++++ .../ios/TabAppearModifier.swift | 4 ++ 5 files changed, 108 insertions(+) create mode 100644 apps/example/src/Examples/LazyTabs.tsx create mode 100644 apps/example/src/Examples/NativeBottomTabsLazy.tsx diff --git a/.gitignore b/.gitignore index 7ce0532c..ab7f80ce 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ android/generated # Codex .codex + +# Agent Device +tmp/ diff --git a/apps/example/src/App.tsx b/apps/example/src/App.tsx index 26b5e752..d789517d 100644 --- a/apps/example/src/App.tsx +++ b/apps/example/src/App.tsx @@ -36,8 +36,13 @@ import NativeBottomTabsUnmounting from './Examples/NativeBottomTabsUnmounting'; import NativeBottomTabsCustomTabBar from './Examples/NativeBottomTabsCustomTabBar'; import NativeBottomTabsFreezeOnBlur from './Examples/NativeBottomTabsFreezeOnBlur'; import NativeBottomTabsScreenLayout from './Examples/NativeBottomTabsScreenLayout'; +import NativeBottomTabsLazy from './Examples/NativeBottomTabsLazy'; import BottomAccessoryView from './Examples/BottomAccessoryView'; import { useLogger } from '@react-navigation/devtools'; +import LazyTabs from './Examples/LazyTabs'; +import { LogBox } from 'react-native'; + +LogBox.ignoreAllLogs(); const HiddenTab = () => { return ; @@ -74,6 +79,7 @@ const FourTabsActiveIndicatorColor = () => { const UnlabeledTabs = () => { return ; }; + const FourTabsRightToLeft = () => { return ; }; @@ -96,6 +102,7 @@ const examples = [ name: 'Embedded stacks', screenOptions: { headerShown: false }, }, + { component: LazyTabs, name: 'Lazy Tabs' }, { component: FourTabsRippleColor, name: 'Four Tabs with ripple Color', @@ -156,6 +163,10 @@ const examples = [ component: NativeBottomTabsScreenLayout, name: 'Native Bottom Tabs with screenLayout', }, + { + component: NativeBottomTabsLazy, + name: 'Native Bottom Tabs with Lazy', + }, { component: NativeBottomTabs, name: 'Native Bottom Tabs' }, { component: JSBottomTabs, name: 'JS Bottom Tabs' }, { diff --git a/apps/example/src/Examples/LazyTabs.tsx b/apps/example/src/Examples/LazyTabs.tsx new file mode 100644 index 00000000..00bfa722 --- /dev/null +++ b/apps/example/src/Examples/LazyTabs.tsx @@ -0,0 +1,47 @@ +import TabView, { SceneMap } from 'react-native-bottom-tabs'; +import { useState } from 'react'; +import { Article } from '../Screens/Article'; +import { Albums } from '../Screens/Albums'; +import { Contacts } from '../Screens/Contacts'; + +const renderScene = SceneMap({ + article: Article, + albums: Albums, + contacts: Contacts, +}); + +export default function LazyTabs() { + const [index, setIndex] = useState(0); + const [routes] = useState([ + { + key: 'article', + title: 'Article', + focusedIcon: require('../../assets/icons/article_dark.png'), + unfocusedIcon: require('../../assets/icons/chat_dark.png'), + badge: '!', + testID: 'articleTestID', + }, + { + key: 'albums', + title: 'Albums', + focusedIcon: require('../../assets/icons/grid_dark.png'), + badge: '5', + testID: 'albumsTestID', + lazy: false, + }, + { + key: 'contacts', + focusedIcon: require('../../assets/icons/person_dark.png'), + title: 'Contacts', + testID: 'contactsTestID', + }, + ]); + + return ( + + ); +} diff --git a/apps/example/src/Examples/NativeBottomTabsLazy.tsx b/apps/example/src/Examples/NativeBottomTabsLazy.tsx new file mode 100644 index 00000000..3a1e3bce --- /dev/null +++ b/apps/example/src/Examples/NativeBottomTabsLazy.tsx @@ -0,0 +1,43 @@ +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'; + +const Tab = createNativeBottomTabNavigator(); + +export default function NativeBottomTabsLazy() { + return ( + + require('../../assets/icons/article_dark.png'), + }} + /> + require('../../assets/icons/grid_dark.png'), + lazy: false, + }} + /> + require('../../assets/icons/person_dark.png'), + }} + /> + require('../../assets/icons/chat_dark.png'), + }} + /> + + ); +} diff --git a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift index 4d8aa04d..c977bbe8 100644 --- a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift @@ -18,6 +18,10 @@ struct TabAppearModifier: ViewModifier { #endif #if os(iOS) + if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { + context.onSelect(context.tabData.key) + } + // Sync selection for tabs nested under the system "More" tab, which // only exists when there are more than 5 visible tabs. Without the // count guard the 5th tab (index 4) of a non-overflowing bar would From fc84bbd77b05510aae0836cc3f0908fb52c010c0 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Tue, 9 Jun 2026 12:29:48 +0100 Subject: [PATCH 2/8] chore: reorder blocks --- .../react-native-bottom-tabs/ios/TabAppearModifier.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift index c977bbe8..3f5e29db 100644 --- a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift @@ -18,10 +18,6 @@ struct TabAppearModifier: ViewModifier { #endif #if os(iOS) - if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { - context.onSelect(context.tabData.key) - } - // Sync selection for tabs nested under the system "More" tab, which // only exists when there are more than 5 visible tabs. Without the // count guard the 5th tab (index 4) of a non-overflowing bar would @@ -29,6 +25,11 @@ struct TabAppearModifier: ViewModifier { // bar is re-shown after `.hideTabBar`), hijacking the selection. if context.props.filteredItems.count > 5, context.index >= 4, context.props.selectedPage != context.tabData.key { context.onSelect(context.tabData.key) + return + } + + if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { + context.onSelect(context.tabData.key) } #endif } From f7abe3147ced9de23ea4bfa686545373a869c5c7 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Tue, 9 Jun 2026 15:52:28 +0100 Subject: [PATCH 3/8] fix(iOS): implement tabBarController(_:shouldSelectTab:) for iOS 27 --- .../ios/TabAppearModifier.swift | 10 ++- .../ios/TabItemEventModifier.swift | 73 +++++++++++++++++-- .../ios/TabViewImpl.swift | 5 +- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift index 3f5e29db..8a7834e5 100644 --- a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift @@ -23,14 +23,16 @@ struct TabAppearModifier: ViewModifier { // count guard the 5th tab (index 4) of a non-overflowing bar would // force-select itself whenever its scene re-appears (e.g. when the tab // bar is re-shown after `.hideTabBar`), hijacking the selection. - if context.props.filteredItems.count > 5, context.index >= 4, context.props.selectedPage != context.tabData.key { + if context.props.filteredItems.count > 5, context.index >= 4, + context.props.selectedPage != context.tabData.key + { context.onSelect(context.tabData.key) return } - if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { - context.onSelect(context.tabData.key) - } +// if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { +// context.onSelect(context.tabData.key) +// } #endif } } diff --git a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift index 42eb1e64..96aecb00 100644 --- a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift @@ -7,7 +7,8 @@ import UIKit #if !os(macOS) && !os(visionOS) private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { - var onClick: ((_ index: Int) -> Bool)? + var onClick: ((_ index: Int?, _ identifier: String?) -> Bool)? + private var currentSelectionDecision: (identifier: String, defaultPrevented: Bool)? func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { #if os(iOS) @@ -21,7 +22,10 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { if isReselectingSameTab { if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { - _ = onClick?(index) + _ = handleSelection( + index: index, + identifier: tabIdentifier(for: viewController, in: tabBarController) + ) } return false @@ -31,17 +35,70 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 // Due to this, whether the tab prevents default has to be defined statically. if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { - let defaultPrevented = onClick?(index) ?? false + let defaultPrevented = handleSelection( + index: index, + identifier: tabIdentifier(for: viewController, in: tabBarController) + ) return !defaultPrevented } return false } + + @available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) + func tabBarController(_ tabBarController: UITabBarController, shouldSelectTab tab: UITab) -> Bool { + let isReselectingSameTab = + tabBarController.selectedTab === tab || + tabBarController.selectedTab?.identifier == tab.identifier + + let defaultPrevented = handleSelection( + index: tabIndex(for: tab, in: tabBarController), + identifier: tab.identifier + ) + + return isReselectingSameTab ? false : !defaultPrevented + } + + private func handleSelection(index: Int?, identifier: String?) -> Bool { + if let identifier, + let decision = currentSelectionDecision, + decision.identifier == identifier { + return decision.defaultPrevented + } + + let defaultPrevented = onClick?(index, identifier) ?? false + + if let identifier { + currentSelectionDecision = (identifier, defaultPrevented) + DispatchQueue.main.async { [weak self] in + if self?.currentSelectionDecision?.identifier == identifier { + self?.currentSelectionDecision = nil + } + } + } + + return defaultPrevented + } + + private func tabIdentifier(for viewController: UIViewController, in tabBarController: UITabBarController) -> String? { + if #available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) { + return tabBarController.tabs.first { $0.viewController === viewController }?.identifier + } + + return nil + } + + @available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) + private func tabIndex(for tab: UITab, in tabBarController: UITabBarController) -> Int? { + tabBarController.tabs.firstIndex { + $0 === tab || $0.identifier == tab.identifier + } + } } struct TabItemEventModifier: ViewModifier { - let onTabEvent: (_ key: Int, _ isLongPress: Bool) -> Bool + let onTabEvent: (_ index: Int?, _ identifier: String?, _ isLongPress: Bool) -> Bool private let delegate = TabBarDelegate() func body(content: Content) -> some View { @@ -52,8 +109,8 @@ struct TabItemEventModifier: ViewModifier { } func handle(tabController: UITabBarController) { - delegate.onClick = { index in - onTabEvent(index, false) + delegate.onClick = { index, identifier in + onTabEvent(index, identifier, false) } tabController.delegate = delegate @@ -70,7 +127,7 @@ struct TabItemEventModifier: ViewModifier { } // Create gesture handler - let handler = LongPressGestureHandler(tabBar: tabController.tabBar) { key, isLongPress in _ = onTabEvent(key, isLongPress) } + let handler = LongPressGestureHandler(tabBar: tabController.tabBar) { index, isLongPress in _ = onTabEvent(index, nil, isLongPress) } let gesture = UILongPressGestureRecognizer(target: handler, action: #selector(LongPressGestureHandler.handleLongPress(_:))) gesture.minimumPressDuration = 0.5 @@ -122,7 +179,7 @@ extension View { /** Event for tab items. Returns true if should prevent default (switching tabs). */ - func onTabItemEvent(_ handler: @escaping (Int, Bool) -> Bool) -> some View { + func onTabItemEvent(_ handler: @escaping (Int?, String?, Bool) -> Bool) -> some View { modifier(TabItemEventModifier(onTabEvent: handler)) } } diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift index 1d3e7db2..14732fdd 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift @@ -46,8 +46,9 @@ struct TabViewImpl: View { tabContent .tabBarMinimizeBehavior(props.minimizeBehavior) #if !os(tvOS) && !os(macOS) && !os(visionOS) - .onTabItemEvent { index, isLongPress in - let item = props.filteredItems[safe: index] + .onTabItemEvent { index, identifier, isLongPress in + let item = identifier.flatMap { props.filteredItems.findByKey($0) } + ?? index.flatMap { props.filteredItems[safe: $0] } guard let key = item?.key else { return false } if isLongPress { From 457c4b71e27a6cb31cb14e8e883ad89eb848ebf1 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Tue, 9 Jun 2026 15:55:26 +0100 Subject: [PATCH 4/8] chore: restore TabAppearModifier --- .../react-native-bottom-tabs/ios/TabAppearModifier.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift index 8a7834e5..4d8aa04d 100644 --- a/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabAppearModifier.swift @@ -23,16 +23,9 @@ struct TabAppearModifier: ViewModifier { // count guard the 5th tab (index 4) of a non-overflowing bar would // force-select itself whenever its scene re-appears (e.g. when the tab // bar is re-shown after `.hideTabBar`), hijacking the selection. - if context.props.filteredItems.count > 5, context.index >= 4, - context.props.selectedPage != context.tabData.key - { + if context.props.filteredItems.count > 5, context.index >= 4, context.props.selectedPage != context.tabData.key { context.onSelect(context.tabData.key) - return } - -// if #available(iOS 27.0, *), context.props.selectedPage != context.tabData.key { -// context.onSelect(context.tabData.key) -// } #endif } } From 53037428f23320a7788704d043e4753f9dce0d23 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 10 Jun 2026 08:58:18 +0100 Subject: [PATCH 5/8] chore: simplify solution --- .../ios/TabItemEventModifier.swift | 63 ++++++------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift index 96aecb00..6d01111f 100644 --- a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift @@ -8,9 +8,13 @@ import UIKit private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { var onClick: ((_ index: Int?, _ identifier: String?) -> Bool)? - private var currentSelectionDecision: (identifier: String, defaultPrevented: Bool)? func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { + if #available(iOS 27.0, tvOS 27.0, *) { + // iOS 27 routes SwiftUI TabView selection through shouldSelectTab. + return true + } + #if os(iOS) // Handle "More" Tab if tabBarController.moreNavigationController == viewController { @@ -22,23 +26,14 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { if isReselectingSameTab { if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { - _ = handleSelection( - index: index, - identifier: tabIdentifier(for: viewController, in: tabBarController) - ) + _ = onClick?(index, nil) } return false } - // Unfortunately, due to iOS 26 new tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. - // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 - // Due to this, whether the tab prevents default has to be defined statically. if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { - let defaultPrevented = handleSelection( - index: index, - identifier: tabIdentifier(for: viewController, in: tabBarController) - ) + let defaultPrevented = onClick?(index, nil) ?? false return !defaultPrevented } @@ -48,47 +43,25 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { @available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) func tabBarController(_ tabBarController: UITabBarController, shouldSelectTab tab: UITab) -> Bool { + guard #available(iOS 27.0, tvOS 27.0, *) else { + return true + } + let isReselectingSameTab = tabBarController.selectedTab === tab || tabBarController.selectedTab?.identifier == tab.identifier - let defaultPrevented = handleSelection( - index: tabIndex(for: tab, in: tabBarController), - identifier: tab.identifier - ) + // Unfortunately, due to modern tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. + // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 + // Due to this, whether the tab prevents default has to be defined statically. + let defaultPrevented = onClick?( + tabIndex(for: tab, in: tabBarController), + tab.identifier + ) ?? false return isReselectingSameTab ? false : !defaultPrevented } - private func handleSelection(index: Int?, identifier: String?) -> Bool { - if let identifier, - let decision = currentSelectionDecision, - decision.identifier == identifier { - return decision.defaultPrevented - } - - let defaultPrevented = onClick?(index, identifier) ?? false - - if let identifier { - currentSelectionDecision = (identifier, defaultPrevented) - DispatchQueue.main.async { [weak self] in - if self?.currentSelectionDecision?.identifier == identifier { - self?.currentSelectionDecision = nil - } - } - } - - return defaultPrevented - } - - private func tabIdentifier(for viewController: UIViewController, in tabBarController: UITabBarController) -> String? { - if #available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) { - return tabBarController.tabs.first { $0.viewController === viewController }?.identifier - } - - return nil - } - @available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) private func tabIndex(for tab: UITab, in tabBarController: UITabBarController) -> Int? { tabBarController.tabs.firstIndex { From 6b67bd7bc1b18cf2716c32f6e4cc096173775e0c Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 10 Jun 2026 09:10:50 +0100 Subject: [PATCH 6/8] chore: cleanup --- .../ios/TabItemEventModifier.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift index 6d01111f..0d67711b 100644 --- a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift @@ -10,7 +10,7 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { var onClick: ((_ index: Int?, _ identifier: String?) -> Bool)? func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { - if #available(iOS 27.0, tvOS 27.0, *) { + if #available(iOS 27.0, *) { // iOS 27 routes SwiftUI TabView selection through shouldSelectTab. return true } @@ -32,6 +32,9 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { return false } + // Unfortunately, due to modern tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. + // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 + // Due to this, whether the tab prevents default has to be defined statically. if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { let defaultPrevented = onClick?(index, nil) ?? false @@ -43,7 +46,7 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { @available(iOS 18.0, tvOS 18.0, visionOS 2.0, *) func tabBarController(_ tabBarController: UITabBarController, shouldSelectTab tab: UITab) -> Bool { - guard #available(iOS 27.0, tvOS 27.0, *) else { + guard #available(iOS 27.0, *) else { return true } From 6bc846c3483d38361467c4bdd53d0f8177131f13 Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 10 Jun 2026 09:12:42 +0100 Subject: [PATCH 7/8] chore: cleanup --- .../react-native-bottom-tabs/ios/TabItemEventModifier.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift index 0d67711b..674e602d 100644 --- a/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift +++ b/packages/react-native-bottom-tabs/ios/TabItemEventModifier.swift @@ -32,7 +32,7 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { return false } - // Unfortunately, due to modern tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. + // Unfortunately, due to iOS 26 new tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 // Due to this, whether the tab prevents default has to be defined statically. if let index = tabBarController.viewControllers?.firstIndex(of: viewController) { @@ -54,7 +54,7 @@ private final class TabBarDelegate: NSObject, UITabBarControllerDelegate { tabBarController.selectedTab === tab || tabBarController.selectedTab?.identifier == tab.identifier - // Unfortunately, due to modern tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. + // Unfortunately, due to iOS 26 new tab switching animations, controlling state from JavaScript is causing significant delays when switching tabs. // See: https://github.com/callstackincubator/react-native-bottom-tabs/issues/383 // Due to this, whether the tab prevents default has to be defined statically. let defaultPrevented = onClick?( From 9c6aad8ba9d62750a53cb415a9e39ab9fd08fa2b Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 10 Jun 2026 09:19:06 +0100 Subject: [PATCH 8/8] chore: add changeset --- .changeset/cute-carrots-switch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cute-carrots-switch.md diff --git a/.changeset/cute-carrots-switch.md b/.changeset/cute-carrots-switch.md new file mode 100644 index 00000000..5a25d953 --- /dev/null +++ b/.changeset/cute-carrots-switch.md @@ -0,0 +1,5 @@ +--- +'react-native-bottom-tabs': patch +--- + +Fix screen not rendering after tab change on iOS 27