diff --git a/.changeset/public-foxes-cheer.md b/.changeset/public-foxes-cheer.md
new file mode 100644
index 00000000..9a4be2ae
--- /dev/null
+++ b/.changeset/public-foxes-cheer.md
@@ -0,0 +1,6 @@
+---
+'react-native-bottom-tabs': minor
+'@bottom-tabs/react-navigation': minor
+---
+
+feat: add ios tab roles
diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock
index b0a038e8..ce8d0a32 100644
--- a/apps/example/ios/Podfile.lock
+++ b/apps/example/ios/Podfile.lock
@@ -1205,71 +1205,7 @@ PODS:
- SwiftUIIntrospect (~> 1.0)
- Yoga
- react-native-safe-area-context (5.2.0):
- - DoubleConversion
- - glog
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-ImageManager
- - React-jsi
- - react-native-safe-area-context/common (= 5.2.0)
- - react-native-safe-area-context/fabric (= 5.2.0)
- - React-NativeModulesApple
- - React-RCTFabric
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-safe-area-context/common (5.2.0):
- - DoubleConversion
- - glog
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- - react-native-safe-area-context/fabric (5.2.0):
- - DoubleConversion
- - glog
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-ImageManager
- - React-jsi
- - react-native-safe-area-context/common
- - React-NativeModulesApple
- - React-RCTFabric
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - Yoga
- React-nativeconfig (0.77.1)
- React-NativeModulesApple (0.77.1):
- glog
@@ -1557,7 +1493,6 @@ PODS:
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- - React-RCTAppDelegate
- React-RCTFabric
- React-rendererdebug
- React-utils
@@ -1591,29 +1526,6 @@ PODS:
- ReactCommon/turbomodule/core
- Yoga
- RNScreens (4.9.1):
- - DoubleConversion
- - glog
- - RCT-Folly (= 2024.11.18.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-RCTImage
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - RNScreens/common (= 4.9.1)
- - Yoga
- - RNScreens/common (4.9.1):
- DoubleConversion
- glog
- RCT-Folly (= 2024.11.18.00)
@@ -1695,7 +1607,6 @@ DEPENDENCIES:
- React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`)
- React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`)
- React-jsc (from `../node_modules/react-native/ReactCommon/jsc`)
- - React-jsc/Fabric (from `../node_modules/react-native/ReactCommon/jsc`)
- React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`)
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
@@ -1902,71 +1813,71 @@ SPEC CHECKSUMS:
FBLazyVector: 79c4b7ec726447eec5f8593379466bd9fde1aa14
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8
- RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809
+ RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82
RCTDeprecation: 664055db806cce35c3c1b43c84414dd66e117ae6
RCTRequired: dc9a83fa1012054f94430d210337ca3a1afe6fc0
RCTTypeSafety: 031cefa254a1df313a196f105b8fcffdab1c5ab6
React: 8edfc46c315852ec88ea4a29d5e79019af3dc667
React-callinvoker: 4450b01574dfc7a8f074f7e29e6965ac04859c8f
- React-Core: a318cda2bd04acffe4f70703098625b201aa2237
- React-CoreModules: ebe93fa403bbd4d0909de105ffd34eeaad355083
- React-cxxreact: 6fe3b8f8e8baf6a22fc39c8c4b4a0e1b5ae3e374
+ React-Core: 1fcd0d52ae09bdf7cf1fe96dd94b082208e43b86
+ React-CoreModules: 78e04d2319b1b61e0d4ed7fcd3e366d461819279
+ React-cxxreact: f9ca69323c1a9c22756ad1a4ed629fb6b44b2a18
React-debug: b0f7271aeacc2eb9e34f863397dcfc204ef721c0
- React-defaultsnativemodule: 07704c5e30a5e66065a46aa11d4014941917a8ee
- React-domnativemodule: dc3492f56861c82e658ea7db60316f7f1a8bace7
- React-Fabric: 7a3d2abb0607f100881cc6c8e54484324c109260
- React-FabricComponents: 4206a041e4671277d45deaf89e52f20951a8dd7d
- React-FabricImage: b05580c4f17de740c38dd5e54d289e913f5bc5df
+ React-defaultsnativemodule: e790bf1d1a300a23504257f306a6629d2a60d845
+ React-domnativemodule: adcfebf6c3b7882b28a061ed2789777f3d337a18
+ React-Fabric: ecf387c8cca0e7ed2ef5104cd2725f38409163b2
+ React-FabricComponents: f7cd4fab1308f52c418b474dc877b94f7acc5672
+ React-FabricImage: e5c94c5679f2fec2261e76809aa86765fb4e0322
React-featureflags: 23d3dcdac6c9badeeb631db8a0883c7a3108d580
- React-featureflagsnativemodule: 75c84559ad5fe3f26ccccdd34ee0320bbf191e4b
- React-graphics: 61380f6d01a225af9a3808dfd0f16622d2b6f90d
- React-idlecallbacksnativemodule: 58ef763402c13067c0e85c615caf7b389d661435
- React-ImageManager: bab699b4ed44ce23b23d5bcab1cdc376eb69d583
+ React-featureflagsnativemodule: 7fc7346e83f792b6cbc851be03cbf201601a81fc
+ React-graphics: 348400b8ba57611d552af6db5dc7d42ccf132d08
+ React-idlecallbacksnativemodule: 8a111e8e0be17ef628ea58ce1c1b1587e331fc51
+ React-ImageManager: ee8526b1af93152133709104c6d649d5dada63b3
React-jsc: 1f6b8e576f2858c5479683647c081de145ce8055
- React-jserrorhandler: f46bec9688c2fd853d101e7fb39ee48e162d5077
- React-jsi: 917f26392eaec18d7ce4e197eb87f680ca87e426
- React-jsiexecutor: 73a715d55e8ee66377ffe831063c5b7bb1a81448
- React-jsinspector: e4ba333f8ed2bf14f0f5459482a28ea922145169
- React-jsitracing: 838bbd073e24e84cf936354f085721cbc9204d70
- React-logger: 1935d6e6461e9c8be4c87af56c56a4876021171e
- React-Mapbuffer: 212171f037e3b22e6c2df839aa826806da480b85
- React-microtasksnativemodule: a0ffd165c39251512d8cf51e9e8f5719dabc38b6
- react-native-bottom-tabs: 6ee03990297f7e37f5c1dd6f5259cee851733d4f
- react-native-safe-area-context: e54b360402f089600c2fb0d825d1d3d918b99e15
+ React-jserrorhandler: 51806588d8259e44cab7f35e72468be6ab8f6798
+ React-jsi: a75033c737fbcb46d80c80fc20b9475bfbf8d2bf
+ React-jsiexecutor: ed2125b6786f75b40cc5e3da791d7ab78a13e711
+ React-jsinspector: 22c5bd5e056328a95678e8f8df9e757171dd21fd
+ React-jsitracing: 9e7066f99151f99ed588f2055e011845b12a1bf6
+ React-logger: e7eeebaed32b88dcc29b10901aa8c5822dc397c4
+ React-Mapbuffer: 73dd1210c4ecf0dfb4e2d4e06f2a13f824a801a9
+ React-microtasksnativemodule: dece61f766f8d326099d217603b1ebb50d6bb707
+ react-native-bottom-tabs: 537b63b00fc50b296da66c1e5199cde4efce9e88
+ react-native-safe-area-context: 3e33e7c43c8b74dba436a5a32651cb8d7064c740
React-nativeconfig: cb207ebba7cafce30657c7ad9f1587a8f32e4564
- React-NativeModulesApple: 76a5d35322908fbc88871e6dd20433bea2b8b2db
- React-perflogger: 8152bab3f0eb4b8751f282f9af7caed2c823a9ea
- React-performancetimeline: 3ef4a640b56f9c7ec5f52bd93217b9b607c37cf4
+ React-NativeModulesApple: 38f252170af5351c88bc2e94d697359cd8c031e6
+ React-perflogger: c4c3b7c18f8a50cdbe2bcdd2f15705ba029a5a02
+ React-performancetimeline: 38bda258bd9f9da19b27615e8edfbec064aa42cc
React-RCTActionSheet: 0fdf55fb8724856d63ca8c63cdb4e2325e15e8ec
- React-RCTAnimation: b93f5a1675cc2599e96851fec13c909fdfb1d6bb
- React-RCTAppDelegate: e3127aff7db7100ee0000e3f67956e9c6cbaa13f
- React-RCTBlob: 53dc2afa8ccdc1b6d6885d81f6862fcb918a1875
- React-RCTFabric: 0a4c2a18d0ef3368f900dc08ea15ab532dd3dcf3
- React-RCTFBReactNativeSpec: ec50e74af2993fb51c1f9991cc7226fea21aaa26
- React-RCTImage: 028171a4d7017ea96a2e605c817cd76f01ed3836
- React-RCTLinking: e3f5431ab5f8f56b82387d41a2c484a278a8e645
- React-RCTNetwork: 6de20da228ffe8bd9c9e3bafe3f7d1dfe1d7bd55
- React-RCTSettings: 433c9f6a070bcecbe5a44d5009326b4d6f3b0667
- React-RCTText: 46249950f8d8738b90a60883d19b5bef09f0a296
- React-RCTVibration: 8f41e85ab6d40c7db6111ca9e8c7492c8de374fb
+ React-RCTAnimation: b2fcc7c462f1fb5e195a5547f6e405ec9a60d80f
+ React-RCTAppDelegate: 7691b8fe13c1c329cfcb44252cd1abf4409c0fcf
+ React-RCTBlob: f6620374c96915ce1762405b1504e607e239c518
+ React-RCTFabric: 8b25a4b9e5b62b4e263a2e85e10bd3215bee6b32
+ React-RCTFBReactNativeSpec: bc3fb34e6f7736cabda3c3de2444b928e3a3dd55
+ React-RCTImage: f189ae651e3c97879b4cdefcba1d4cffe55439da
+ React-RCTLinking: 759ac5e4aed95ac3c29849f98ff3f3b5ece830ed
+ React-RCTNetwork: ce1f38434a70eb1e228344f7632e636c3ceca03b
+ React-RCTSettings: 3602ea3adf9009f6d09461bf05f7e392414c32d8
+ React-RCTText: e48b4b54eab3f4cfea9be1228b5ef9ad3b8172c1
+ React-RCTVibration: 2e4dc335dd1e57c7004bcc07e7f5319e5968d5cf
React-rendererconsistency: c766ce7261ab6ed6be7bc155c403e29436d4f156
- React-rendererdebug: 1f619b295f346242842f3accee23e8394b995d3c
+ React-rendererdebug: f8bf864b2646944c3f7c41555dbed0b5d7aea5d1
React-rncore: cafe45e14d870bbecbbf4bd89e12ef3b596e1f2d
- React-RuntimeApple: 51303fe6715be3596bf0479c1b34ce56d61ac81f
- React-RuntimeCore: a0fe52c5f42a65f9d636ee4fbee14322865eb530
+ React-RuntimeApple: 6b67a8f0109a5289ccef380d14ba099aeadcef0e
+ React-RuntimeCore: 056d99b829e1de4afed419e17e95639cf72799f1
React-runtimeexecutor: 201311bdafb53b5c30292782c8ee90193af86d91
- React-runtimescheduler: 89a12fb995740bf1c1d768d3c6732e709913dbeb
+ React-runtimescheduler: 32e558eb10b88ea398bb974b74d2230e5a71f30e
React-timing: 127d8598b5a15ae5b29ebd0ec474d590285c6f2f
- React-utils: 5157cba7e171651af2113558b0c6cd562d4271bf
- ReactAppDependencyProvider: e7e92253013754a8c35ebdbf8ad700f4e8956f62
- ReactCodegen: 6efd314e2f59c2eae0898c6d1e0d933876a1666c
- ReactCommon: cec0154a884747940be235f16acbd4fc9c959f89
- ReactNativeHost: 9796a3872d3b2777a87acbe62d666dec521eda7b
- ReactTestApp-DevSupport: ba03e8b8d2c87ed4b631ae8dc25765925f37e4a7
+ React-utils: d5269d138fd5b7b93a7f03e697f25d482e64d399
+ ReactAppDependencyProvider: 41e9fb63606c32cce924653d2d410cb01ec81286
+ ReactCodegen: 9cf993a8cfdffca67d5abe1ba056020fc48fc0d4
+ ReactCommon: ede76856e587ac3fd7ce70ca2387e571bc947d14
+ ReactNativeHost: 8ef54ff7c6c17c1242e287c286699f8955f22e45
+ ReactTestApp-DevSupport: f23bacc6d21da29a7d8d248bb6ee8cc9ad241a48
ReactTestApp-Resources: 4f6dff3b157f879757cd750caccd1d34a7eda647
- RNGestureHandler: 3bd32689c176c81dd57bb1e3b3804e7352994000
- RNScreens: be44c347c9ae035bc78da28e283f484fa37e916f
- RNVectorIcons: 5987b681d1ad97637f67e4e7af2902b9d4c3f5d6
+ RNGestureHandler: 00e35ccf0f6ee047d10d726a6b2c1cf85de54946
+ RNScreens: 62107db9c6147f19a6d0e8989f355acae46a99c5
+ RNVectorIcons: 5c1b761ed018ba598757d876380e584414befada
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
diff --git a/apps/example/src/Examples/SFSymbols.tsx b/apps/example/src/Examples/SFSymbols.tsx
index 1e7b48ca..f9490fbb 100644
--- a/apps/example/src/Examples/SFSymbols.tsx
+++ b/apps/example/src/Examples/SFSymbols.tsx
@@ -41,6 +41,7 @@ export default function SFSymbols() {
? require('../../assets/icons/person_dark.png')
: { sfSymbol: 'person.fill' },
title: 'Contacts',
+ role: 'search',
},
]);
diff --git a/docs/docs/docs/guides/standalone-usage.md b/docs/docs/docs/guides/standalone-usage.md
index 545b9f25..72ddb643 100644
--- a/docs/docs/docs/guides/standalone-usage.md
+++ b/docs/docs/docs/guides/standalone-usage.md
@@ -212,6 +212,7 @@ Each route in the `routes` array can have the following properties:
- `activeTintColor`: Custom active tint color for this specific tab
- `lazy`: Whether to lazy load this tab's content
- `freezeOnBlur`: Whether to freeze the tab's content when it's not visible
+- `role`: A value that defines the purpose of the tab
### Helper Props
@@ -260,3 +261,9 @@ Function to determine if a tab should be hidden.
Function to get the test ID for a tab item.
- Default: Uses `route.testID`
+
+#### `getRole`
+
+Function to get the role for a tab item.
+
+- Default: Uses `route.role`
diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx
index c04a8c95..97a16d2b 100644
--- a/docs/docs/docs/guides/usage-with-react-navigation.mdx
+++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx
@@ -326,6 +326,14 @@ It's working separately from `enableFreeze()` in `react-native-screens`. So sett
Test ID for the tab item. This can be used to find the tab item in the native view hierarchy.
+#### `role`
+
+A value that defines the purpose of the tab. This can be used to pin and separate search tabs
+
+Available options:
+
+- `search` - The search role.
+
### Events
The navigator can emit events on certain actions. Supported events are:
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
index 0e6df67b..5a16130f 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
@@ -16,7 +16,7 @@ data class TabInfo(
val badge: String?,
val activeTintColor: Int?,
val hidden: Boolean,
- val testID: String?
+ val testID: String?,
)
class RCTTabViewImpl {
diff --git a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
index ff511604..1cfc0167 100644
--- a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
+++ b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm
@@ -37,7 +37,8 @@
lhs.badge == rhs.badge &&
lhs.activeTintColor == rhs.activeTintColor &&
lhs.hidden == rhs.hidden &&
- lhs.testID == rhs.testID;
+ lhs.testID == rhs.testID &&
+ lhs.role == rhs.role;
}
bool operator!=(const RNCTabViewItemsStruct& lhs, const RNCTabViewItemsStruct& rhs) {
@@ -201,7 +202,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
sfSymbol:RCTNSStringFromStringNilIfEmpty(item.sfSymbol)
activeTintColor:RCTUIColorFromSharedColor(item.activeTintColor)
hidden:item.hidden
- testID:RCTNSStringFromStringNilIfEmpty(item.testID)];
+ testID:RCTNSStringFromStringNilIfEmpty(item.testID)
+ role:RCTNSStringFromStringNilIfEmpty(item.role)];
[result addObject:tabInfo];
}
diff --git a/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift b/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift
index 01754e79..750a9be4 100644
--- a/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift
+++ b/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift
@@ -17,6 +17,7 @@ struct NewTabView: AnyTabView {
if !tabData.hidden || isFocused {
let icon = props.icons[index]
+ let role = tabData.role?.convert() ?? nil
let platformChild = props.children[safe: index] ?? PlatformView()
let child = RepresentableView(view: platformChild)
@@ -28,7 +29,7 @@ struct NewTabView: AnyTabView {
onSelect: onSelect
)
- Tab(value: tabData.key) {
+ Tab(value: tabData.key, role: role) {
child
.ignoresSafeArea(.container, edges: .all)
.tabAppear(using: context)
diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift
index 833aca94..28d1063e 100644
--- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift
+++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift
@@ -23,6 +23,18 @@ internal enum MinimizeBehavior: String {
#endif
}
+public enum TabBarRole: String {
+ case search
+
+ @available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
+ func convert() -> TabRole {
+ switch self {
+ case .search:
+ return .search
+ }
+ }
+}
+
/**
Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes.
*/
diff --git a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
index 9a304713..7914f231 100644
--- a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
+++ b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
@@ -13,6 +13,7 @@ public final class TabInfo: NSObject {
public let activeTintColor: PlatformColor?
public let hidden: Bool
public let testID: String?
+ public let role: TabBarRole?
public init(
key: String,
@@ -21,7 +22,8 @@ public final class TabInfo: NSObject {
sfSymbol: String,
activeTintColor: PlatformColor?,
hidden: Bool,
- testID: String?
+ testID: String?,
+ role: String?
) {
self.key = key
self.title = title
@@ -30,6 +32,7 @@ public final class TabInfo: NSObject {
self.activeTintColor = activeTintColor
self.hidden = hidden
self.testID = testID
+ self.role = TabBarRole(rawValue: role ?? "")
super.init()
}
}
@@ -273,7 +276,8 @@ public final class TabInfo: NSObject {
sfSymbol: itemDict["sfSymbol"] as? String ?? "",
activeTintColor: RCTConvert.uiColor(itemDict["activeTintColor"] as? NSNumber),
hidden: itemDict["hidden"] as? Bool ?? false,
- testID: itemDict["testID"] as? String ?? ""
+ testID: itemDict["testID"] as? String ?? "",
+ role: itemDict["role"] as? String,
)
)
}
diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx
index ed30dd13..15b28b69 100644
--- a/packages/react-native-bottom-tabs/src/TabView.tsx
+++ b/packages/react-native-bottom-tabs/src/TabView.tsx
@@ -20,7 +20,7 @@ import { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
import NativeTabView from './TabViewNativeComponent';
import useLatestCallback from 'use-latest-callback';
-import type { BaseRoute, NavigationState } from './types';
+import type { BaseRoute, NavigationState, TabRole } from './types';
import DelayedFreeze from './DelayedFreeze';
const isAppleSymbol = (icon: any): icon is { sfSymbol: string } =>
@@ -124,6 +124,11 @@ interface Props {
*/
getTestID?: (props: { route: Route }) => string | undefined;
+ /**
+ * Get role for the tab, uses `route.role` by default. (iOS only)
+ */
+ getRole?: (props: { route: Route }) => TabRole | undefined;
+
/**
* Custom tab bar to render. Set to `null` to hide the tab bar completely.
*/
@@ -190,6 +195,7 @@ const TabView = ({
getHidden = ({ route }: { route: Route }) => route.hidden,
getActiveTintColor = ({ route }: { route: Route }) => route.activeTintColor,
getTestID = ({ route }: { route: Route }) => route.testID,
+ getRole = ({ route }: { route: Route }) => route.role,
hapticFeedbackEnabled = false,
// Android's native behavior is to show labels when there are less than 4 tabs. We leave it as undefined to use the platform default behavior.
labeled = Platform.OS !== 'android' ? true : undefined,
@@ -261,6 +267,7 @@ const TabView = ({
activeTintColor: processColor(getActiveTintColor({ route })),
hidden: getHidden?.({ route }),
testID: getTestID?.({ route }),
+ role: getRole?.({ route }),
};
}),
[
@@ -271,6 +278,7 @@ const TabView = ({
getActiveTintColor,
getHidden,
getTestID,
+ getRole,
]
);
diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
index 4eca038b..a91a6ed9 100644
--- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
+++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
@@ -30,6 +30,7 @@ export type TabViewItems = ReadonlyArray<{
activeTintColor?: ProcessedColorValue | null;
hidden?: boolean;
testID?: string;
+ role?: string;
}>;
export interface TabViewProps extends ViewProps {
diff --git a/packages/react-native-bottom-tabs/src/index.tsx b/packages/react-native-bottom-tabs/src/index.tsx
index 58090804..408f08f0 100644
--- a/packages/react-native-bottom-tabs/src/index.tsx
+++ b/packages/react-native-bottom-tabs/src/index.tsx
@@ -15,4 +15,4 @@ export { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
/**
* Types
*/
-export type { AppleIcon } from './types';
+export type { AppleIcon, TabRole } from './types';
diff --git a/packages/react-native-bottom-tabs/src/types.ts b/packages/react-native-bottom-tabs/src/types.ts
index 744af8df..bd47f95d 100644
--- a/packages/react-native-bottom-tabs/src/types.ts
+++ b/packages/react-native-bottom-tabs/src/types.ts
@@ -5,6 +5,8 @@ export type IconSource = string | ImageSourcePropType;
export type AppleIcon = { sfSymbol: SFSymbol };
+export type TabRole = 'search';
+
export type BaseRoute = {
key: string;
title?: string;
@@ -15,6 +17,7 @@ export type BaseRoute = {
activeTintColor?: string;
hidden?: boolean;
testID?: string;
+ role?: TabRole;
freezeOnBlur?: boolean;
};
diff --git a/packages/react-navigation/src/types.ts b/packages/react-navigation/src/types.ts
index 4a5192fa..32c48064 100644
--- a/packages/react-navigation/src/types.ts
+++ b/packages/react-navigation/src/types.ts
@@ -9,7 +9,7 @@ import type {
} from '@react-navigation/native';
import type { ImageSourcePropType } from 'react-native';
import type TabView from 'react-native-bottom-tabs';
-import type { AppleIcon } from 'react-native-bottom-tabs';
+import type { AppleIcon, TabRole } from 'react-native-bottom-tabs';
export type NativeBottomTabNavigationEventMap = {
/**
@@ -92,6 +92,11 @@ export type NativeBottomTabNavigationOptions = {
*/
tabBarButtonTestID?: string;
+ /**
+ * Role for the tab. (iOS only)
+ */
+ role?: TabRole;
+
/**
* Whether inactive screens should be suspended from re-rendering. Defaults to `false`.
*/
@@ -131,6 +136,7 @@ export type NativeBottomTabNavigationConfig = Partial<
| 'onTabLongPress'
| 'getActiveTintColor'
| 'getTestID'
+ | 'getRole'
| 'tabBar'
| 'getFreezeOnBlur'
>
diff --git a/packages/react-navigation/src/views/NativeBottomTabView.tsx b/packages/react-navigation/src/views/NativeBottomTabView.tsx
index b3965d16..ffc3b41e 100644
--- a/packages/react-navigation/src/views/NativeBottomTabView.tsx
+++ b/packages/react-navigation/src/views/NativeBottomTabView.tsx
@@ -49,6 +49,7 @@ export default function NativeBottomTabView({
getTestID={({ route }) =>
descriptors[route.key]?.options.tabBarButtonTestID
}
+ getRole={({ route }) => descriptors[route.key]?.options.role}
tabBar={
tabBar ? () => tabBar({ state, descriptors, navigation }) : undefined
}