diff --git a/.changeset/tidy-eels-wish.md b/.changeset/tidy-eels-wish.md
new file mode 100644
index 00000000..b5ae190b
--- /dev/null
+++ b/.changeset/tidy-eels-wish.md
@@ -0,0 +1,5 @@
+---
+'react-native-bottom-tabs': minor
+---
+
+Add `experimental_bakedTintColors` prop to opt into the iOS 26 Liquid Glass active and inactive tint color workaround.
diff --git a/apps/example/src/Examples/TintColors.tsx b/apps/example/src/Examples/TintColors.tsx
index c5f44333..61351eaa 100644
--- a/apps/example/src/Examples/TintColors.tsx
+++ b/apps/example/src/Examples/TintColors.tsx
@@ -4,6 +4,7 @@ import { Article } from '../Screens/Article';
import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
+import { Button, Platform, StyleSheet, View } from 'react-native';
const renderScene = SceneMap({
article: Article,
@@ -12,8 +13,11 @@ const renderScene = SceneMap({
chat: Chat,
});
+const isAndroid = Platform.OS === 'android';
+
export default function TintColorsExample() {
const [index, setIndex] = useState(0);
+ const [bakedTintColors, setBakedTintColors] = useState(false);
const [routes] = useState([
{
key: 'article',
@@ -31,9 +35,11 @@ export default function TintColorsExample() {
},
{
key: 'contacts',
- focusedIcon: require('../../assets/icons/person_dark.png'),
+ focusedIcon: isAndroid
+ ? require('../../assets/icons/person_dark.png')
+ : { sfSymbol: 'person.fill' },
title: 'Contacts',
- activeTintColor: 'yellow',
+ activeTintColor: 'blue',
},
{
key: 'chat',
@@ -45,14 +51,32 @@ export default function TintColorsExample() {
]);
return (
-
+
+
+
+
+
);
}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ controls: {
+ padding: 12,
+ },
+});
diff --git a/docs/docs/docs/guides/standalone-usage.md b/docs/docs/docs/guides/standalone-usage.md
index c184b286..0be26bc4 100644
--- a/docs/docs/docs/guides/standalone-usage.md
+++ b/docs/docs/docs/guides/standalone-usage.md
@@ -184,6 +184,20 @@ Color for inactive tabs.
On iOS >= 26 (Liquid Glass), this prop is ignored.
:::
+#### `experimental_bakedTintColors`
+
+Enable an experimental mode which bakes (rasterizes) the label into the icon image, allowing for better control of the tint colors on iOS 26+.
+
+- Type: `boolean`
+
+:::warning
+This feature is experimental, and might be removed in the future.
+
+It has many drawbacks, such as SFSymbol icon sizes being different on label width, badges being positioned far away from the icon depending on the label width, and possibly breaking accessibility since the label will be baked inside the icon image.
+
+Use with caution, only if you prioritize tint color consistency between platforms.
+:::
+
#### `tabBarStyle`
Object containing styles for the tab bar.
diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx
index c505e942..d7bda582 100644
--- a/docs/docs/docs/guides/usage-with-react-navigation.mdx
+++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx
@@ -132,6 +132,20 @@ Color for the inactive tabs.
On iOS >= 26 (Liquid Glass), this prop is ignored.
:::
+#### `experimental_bakedTintColors`
+
+Enable an experimental mode which bakes (rasterizes) the label into the icon image, allowing for better control of the tint colors on iOS 26+.
+
+- Type: `boolean`
+
+:::warning
+This feature is experimental, and might be removed in the future.
+
+It has many drawbacks, such as SFSymbol icon sizes being different on label width, badges being positioned far away from the icon depending on the label width, and possibly breaking accessibility since the label will be baked inside the icon image.
+
+Use with caution, only if you prioritize tint color consistency between platforms.
+:::
+
#### `tabBarStyle`
Object containing styles for the tab bar.
diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewManager.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewManager.kt
index 3f130e11..95644dbb 100644
--- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewManager.kt
+++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewManager.kt
@@ -151,6 +151,9 @@ class RCTTabViewManager(context: ReactApplicationContext) :
view.setInactiveTintColor(value)
}
+ override fun setExperimentalBakedTintColors(view: ReactBottomNavigationView?, value: Boolean) {
+ }
+
override fun setActiveIndicatorColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null) {
val color = ColorStateList.valueOf(value)
diff --git a/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm b/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
index 92cab0e7..bd8edcef 100644
--- a/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
+++ b/packages/react-native-bottom-tabs/ios/RCTTabViewComponentView.mm
@@ -156,6 +156,10 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
_tabViewProvider.inactiveTintColor = RCTUIColorFromSharedColor(newViewProps.inactiveTintColor);
}
+ if (oldViewProps.experimentalBakedTintColors != newViewProps.experimentalBakedTintColors) {
+ _tabViewProvider.experimentalBakedTintColors = newViewProps.experimentalBakedTintColors;
+ }
+
if (oldViewProps.hapticFeedbackEnabled != newViewProps.hapticFeedbackEnabled) {
_tabViewProvider.hapticFeedbackEnabled = newViewProps.hapticFeedbackEnabled;
}
@@ -262,4 +266,3 @@ - (void)onLayoutWithSize:(CGSize)size reactTag:(NSNumber *)reactTag {
#endif // RCT_NEW_ARCH_ENABLED
-
diff --git a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift
index 4f9ec926..1d3e7db2 100644
--- a/packages/react-native-bottom-tabs/ios/TabViewImpl.swift
+++ b/packages/react-native-bottom-tabs/ios/TabViewImpl.swift
@@ -21,7 +21,7 @@ struct TabViewImpl: View {
onSelect: onSelect
) {
#if !os(macOS)
- updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateTabBarAppearance(props: props, tabBar: tabBar)
#endif
}
} else {
@@ -31,7 +31,7 @@ struct TabViewImpl: View {
onSelect: onSelect
) {
#if !os(macOS)
- updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateTabBarAppearance(props: props, tabBar: tabBar)
#endif
}
}
@@ -61,14 +61,16 @@ struct TabViewImpl: View {
}
#endif
.introspectTabView { tabController in
-#if !os(macOS)
- tabController.view.backgroundColor = .clear
- tabController.viewControllers?.forEach { $0.view.backgroundColor = .clear }
-#endif
+ #if !os(macOS)
+ tabController.view.backgroundColor = .clear
+ tabController.viewControllers?.forEach { $0.view.backgroundColor = .clear }
+ #endif
#if os(macOS)
tabBar = tabController
#else
tabBar = tabController.tabBar
+ updateTabBarAppearance(props: props, tabBar: tabController.tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabController.tabBar)
if !props.tabBarHidden {
onTabBarMeasured(
Int(tabController.tabBar.frame.size.height)
@@ -112,6 +114,20 @@ struct TabViewImpl: View {
}
#if !os(macOS)
+ private func updateExperimentalBakedTintColors(props: TabViewProps, tabBar: UITabBar?) {
+ guard shouldUseExperimentalBakedTintColors(props: props),
+ let tabBar,
+ let items = tabBar.items
+ else { return }
+
+ configureExperimentalBakedTintColors(items: items, props: props)
+
+ DispatchQueue.main.async { [weak tabBar] in
+ guard let tabBar, let items = tabBar.items else { return }
+ configureExperimentalBakedTintColors(items: items, props: props)
+ }
+ }
+
private func updateTabBarAppearance(props: TabViewProps, tabBar: UITabBar?) {
guard let tabBar else { return }
@@ -129,6 +145,7 @@ struct TabViewImpl: View {
#if !os(macOS)
private func configureTransparentAppearance(tabBar: UITabBar, props: TabViewProps) {
tabBar.barTintColor = props.barTintColor
+ tabBar.tintColor = props.selectedActiveTintColor
#if !os(visionOS)
tabBar.isTranslucent = props.translucent
#endif
@@ -136,7 +153,7 @@ struct TabViewImpl: View {
guard let items = tabBar.items else { return }
- let attributes = TabBarFontSize.createNormalStateAttributes(
+ let fontAttributes = TabBarFontSize.createNormalStateAttributes(
fontSize: props.fontSize,
fontFamily: props.fontFamily,
fontWeight: props.fontWeight,
@@ -144,12 +161,15 @@ struct TabViewImpl: View {
)
items.forEach { item in
- item.setTitleTextAttributes(attributes, for: .normal)
+ item.setTitleTextAttributes(fontAttributes, for: .normal)
+ item.setTitleTextAttributes(selectedAttributes(props: props), for: .selected)
}
}
private func configureStandardAppearance(tabBar: UITabBar, props: TabViewProps) {
let appearance = UITabBarAppearance()
+ tabBar.tintColor = props.selectedActiveTintColor
+ tabBar.unselectedItemTintColor = props.effectiveInactiveTintColor
// Configure background
switch props.scrollEdgeAppearance {
@@ -180,8 +200,12 @@ struct TabViewImpl: View {
if let inactiveTintColor = props.effectiveInactiveTintColor {
itemAppearance.normal.iconColor = inactiveTintColor
}
+ if let activeTintColor = props.selectedActiveTintColor {
+ itemAppearance.selected.iconColor = activeTintColor
+ }
itemAppearance.normal.titleTextAttributes = attributes
+ itemAppearance.selected.titleTextAttributes = selectedAttributes(props: props)
// Apply item appearance to all layouts
appearance.stackedLayoutAppearance = itemAppearance
@@ -194,6 +218,186 @@ struct TabViewImpl: View {
tabBar.scrollEdgeAppearance = appearance.copy()
}
}
+
+ private func configureExperimentalBakedTintColors(items: [UITabBarItem], props: TabViewProps) {
+ for (tabBarIndex, item) in items.enumerated() {
+ guard let tabData = props.filteredItems[safe: tabBarIndex],
+ let itemIndex = props.items.firstIndex(where: { $0.key == tabData.key })
+ else { continue }
+
+ let tabActiveColor = tabData.activeTintColor ?? props.activeTintColor
+ let assetIcon = props.icons[itemIndex]
+ let icon = assetIcon ?? makeSFSymbolImage(named: tabData.sfSymbol)
+ let shouldRenderLabelIntoImage =
+ props.hasCustomTintColors && props.labeled && tabData.role != .search && icon != nil
+
+ item.accessibilityLabel = tabData.title
+
+ if shouldRenderLabelIntoImage, let icon {
+ item.title = ""
+ item.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 100)
+ item.image = makeTabBarItemImage(
+ icon: icon,
+ title: tabData.title,
+ color: props.inactiveTintColor,
+ props: props
+ )
+ item.selectedImage = makeTabBarItemImage(
+ icon: icon,
+ title: tabData.title,
+ color: tabActiveColor,
+ props: props
+ )
+ continue
+ }
+
+ item.title = props.labeled ? tabData.title : nil
+ item.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 0)
+
+ if let icon {
+ item.image =
+ props.inactiveTintColor.map {
+ icon.withTintColor($0, renderingMode: .alwaysOriginal)
+ } ?? icon
+ item.selectedImage =
+ tabActiveColor.map {
+ icon.withTintColor($0, renderingMode: .alwaysOriginal)
+ } ?? icon
+ }
+
+ item.setTitleTextAttributes(
+ TabBarFontSize.createFontAttributes(
+ size: props.fontSize.map(CGFloat.init) ?? TabBarFontSize.defaultSize,
+ family: props.fontFamily,
+ weight: props.fontWeight,
+ color: tabActiveColor
+ ),
+ for: .selected
+ )
+ }
+ }
+
+ private func resetExperimentalBakedTintColors(props: TabViewProps, tabBar: UITabBar?) {
+ guard let tabBar,
+ let items = tabBar.items
+ else { return }
+
+ for (tabBarIndex, item) in items.enumerated() {
+ guard let tabData = props.filteredItems[safe: tabBarIndex],
+ let itemIndex = props.items.firstIndex(where: { $0.key == tabData.key })
+ else { continue }
+
+ let assetIcon = props.icons[itemIndex]
+ let icon = assetIcon ?? makeSFSymbolImage(named: tabData.sfSymbol)
+
+ item.title = props.labeled ? tabData.title : nil
+ item.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 0)
+ item.image = icon
+ item.selectedImage = icon
+ }
+ }
+
+ private func makeSFSymbolImage(named sfSymbol: String?) -> UIImage? {
+ guard let sfSymbol, !sfSymbol.isEmpty else { return nil }
+
+ return UIImage(systemName: sfSymbol)
+ }
+
+ private func selectedAttributes(props: TabViewProps) -> [NSAttributedString.Key: Any] {
+ TabBarFontSize.createFontAttributes(
+ size: props.fontSize.map(CGFloat.init) ?? TabBarFontSize.defaultSize,
+ family: props.fontFamily,
+ weight: props.fontWeight,
+ color: props.selectedActiveTintColor
+ )
+ }
+
+ private func shouldUseExperimentalBakedTintColors(props: TabViewProps) -> Bool {
+ guard props.experimentalBakedTintColors else {
+ return false
+ }
+
+ #if os(iOS)
+ if #available(iOS 26.0, *) {
+ return true
+ }
+ #endif
+
+ return false
+ }
+
+ private func makeTabBarItemImage(
+ icon: UIImage,
+ title: String,
+ color: UIColor?,
+ props: TabViewProps
+ ) -> UIImage {
+ let color = color ?? .label
+ let iconSize = CGSize(width: 27, height: 27)
+ let font =
+ TabBarFontSize.createFontAttributes(
+ size: props.fontSize.map(CGFloat.init) ?? TabBarFontSize.defaultSize,
+ family: props.fontFamily,
+ weight: props.fontWeight
+ )[.font] as? UIFont ?? UIFont.boldSystemFont(ofSize: TabBarFontSize.defaultSize)
+ let paragraphStyle = NSMutableParagraphStyle()
+ paragraphStyle.alignment = .center
+ let attributes: [NSAttributedString.Key: Any] = [
+ .font: font,
+ .foregroundColor: color,
+ .paragraphStyle: paragraphStyle,
+ ]
+ let titleSize = (title as NSString).size(withAttributes: attributes)
+ let imageSize = CGSize(
+ width: max(iconSize.width, ceil(titleSize.width)) + 8,
+ height: iconSize.height + 3 + ceil(titleSize.height)
+ )
+ let format = UIGraphicsImageRendererFormat()
+ format.scale = UIScreen.main.scale
+
+ let image = UIGraphicsImageRenderer(size: imageSize, format: format).image { _ in
+ let tintedIcon = icon.withTintColor(color, renderingMode: .alwaysOriginal)
+ let iconFrame = aspectFitRect(
+ size: tintedIcon.size,
+ in: CGRect(
+ x: (imageSize.width - iconSize.width) / 2,
+ y: 0,
+ width: iconSize.width,
+ height: iconSize.height
+ )
+ )
+
+ tintedIcon.draw(in: iconFrame)
+
+ (title as NSString).draw(
+ in: CGRect(
+ x: 0,
+ y: iconSize.height + 3,
+ width: imageSize.width,
+ height: ceil(titleSize.height)
+ ),
+ withAttributes: attributes
+ )
+ }
+
+ return image.withRenderingMode(.alwaysOriginal)
+ }
+
+ private func aspectFitRect(size: CGSize, in rect: CGRect) -> CGRect {
+ guard size.width > 0, size.height > 0 else {
+ return rect
+ }
+
+ let scale = min(rect.width / size.width, rect.height / size.height)
+ let fittedSize = CGSize(width: size.width * scale, height: size.height * scale)
+
+ return CGRect(
+ x: rect.minX + (rect.width - fittedSize.width) / 2,
+ y: rect.minY + (rect.height - fittedSize.height) / 2,
+ width: fittedSize.width,
+ height: fittedSize.height
+ )
+ }
#endif
extension View {
@@ -246,18 +450,40 @@ extension View {
}
.onChange(of: props.inactiveTintColor) { _ in
updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
}
- .onChange(of: props.selectedActiveTintColor) { _ in
+ .onChange(of: props.activeTintColor) { _ in
updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
+ }
+ .onChange(of: props.selectedActiveTintColor) { newValue in
+ tabBar?.tintColor = newValue
+ }
+ .onChange(of: props.iconsRevision) { _ in
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
+ }
+ .onChange(of: props.labeled) { _ in
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
}
.onChange(of: props.fontSize) { _ in
updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
}
.onChange(of: props.fontFamily) { _ in
updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
}
.onChange(of: props.fontWeight) { _ in
updateTabBarAppearance(props: props, tabBar: tabBar)
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
+ }
+ .onChange(of: props.experimentalBakedTintColors) { newValue in
+ updateTabBarAppearance(props: props, tabBar: tabBar)
+ if newValue {
+ updateExperimentalBakedTintColors(props: props, tabBar: tabBar)
+ } else {
+ resetExperimentalBakedTintColors(props: props, tabBar: tabBar)
+ }
}
.onChange(of: props.tabBarHidden) { newValue in
tabBar?.isHidden = newValue
@@ -279,7 +505,7 @@ extension View {
@ViewBuilder
func tabBarMinimizeBehavior(_ behavior: MinimizeBehavior?) -> some View {
#if compiler(>=6.2)
- if #available(iOS 26.0, macOS 26.0, tvOS 26.0, *) {
+ if #available(iOS 26.0, macOS 26.0, tvOS 26.0, *) {
if let behavior {
self.tabBarMinimizeBehavior(behavior.convert())
} else {
diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift
index 5dc0898a..85e4d8d1 100644
--- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift
+++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift
@@ -7,25 +7,25 @@ internal enum MinimizeBehavior: String {
case onScrollUp
case onScrollDown
-#if compiler(>=6.2)
- @available(iOS 26.0, macOS 26.0, tvOS 26.0, *)
- func convert() -> TabBarMinimizeBehavior {
-#if os(macOS) || os(tvOS)
- return .automatic
-#else
- switch self {
- case .automatic:
- return .automatic
- case .never:
- return .never
- case .onScrollUp:
- return .onScrollUp
- case .onScrollDown:
- return .onScrollDown
+ #if compiler(>=6.2)
+ @available(iOS 26.0, macOS 26.0, tvOS 26.0, *)
+ func convert() -> TabBarMinimizeBehavior {
+ #if os(macOS) || os(tvOS)
+ return .automatic
+ #else
+ switch self {
+ case .automatic:
+ return .automatic
+ case .never:
+ return .never
+ case .onScrollUp:
+ return .onScrollUp
+ case .onScrollDown:
+ return .onScrollDown
+ }
+ #endif
}
-#endif
- }
-#endif
+ #endif
}
public enum TabBarRole: String {
@@ -34,8 +34,8 @@ public enum TabBarRole: String {
@available(iOS 18, macOS 15, visionOS 2, tvOS 18, *)
func convert() -> TabRole {
switch self {
- case .search:
- return .search
+ case .search:
+ return .search
}
}
}
@@ -49,14 +49,13 @@ struct IdentifiablePlatformView: Identifiable, Equatable {
}
}
-/**
- Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes.
- */
+/// Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes.
class TabViewProps: ObservableObject {
@Published var children: [IdentifiablePlatformView] = []
@Published var items: [TabInfo] = []
@Published var selectedPage: String?
@Published var icons: [Int: PlatformImage] = [:]
+ @Published var iconsRevision: Int = 0
@Published var sidebarAdaptable: Bool?
@Published var labeled: Bool = false
@Published var minimizeBehavior: MinimizeBehavior?
@@ -64,6 +63,7 @@ class TabViewProps: ObservableObject {
@Published var barTintColor: PlatformColor?
@Published var activeTintColor: PlatformColor?
@Published var inactiveTintColor: PlatformColor?
+ @Published var experimentalBakedTintColors: Bool = false
@Published var translucent: Bool = true
@Published var disablePageAnimations: Bool = false
@Published var hapticFeedbackEnabled: Bool = false
@@ -75,14 +75,21 @@ class TabViewProps: ObservableObject {
var selectedActiveTintColor: PlatformColor? {
if let selectedPage,
- let tabData = items.findByKey(selectedPage),
- let activeTintColor = tabData.activeTintColor {
+ let tabData = items.findByKey(selectedPage),
+ let activeTintColor = tabData.activeTintColor
+ {
return activeTintColor
}
return activeTintColor
}
+ var hasCustomTintColors: Bool {
+ activeTintColor != nil
+ || inactiveTintColor != nil
+ || items.contains(where: { $0.activeTintColor != nil })
+ }
+
var effectiveInactiveTintColor: PlatformColor? {
#if os(iOS)
if ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 26 {
diff --git a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
index deac524d..799f3c0c 100644
--- a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
+++ b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift
@@ -136,6 +136,12 @@ public final class TabInfo: NSObject {
}
}
+ @objc public var experimentalBakedTintColors: Bool = false {
+ didSet {
+ props.experimentalBakedTintColors = experimentalBakedTintColors
+ }
+ }
+
@objc public var fontFamily: NSString? {
didSet {
props.fontFamily = fontFamily as? String
@@ -261,7 +267,17 @@ public final class TabInfo: NSObject {
guard let image else { return }
DispatchQueue.main.async { [weak self] in
guard let self else { return }
- props.icons[index] = image.resizeImageTo(size: iconSize)
+ let icon = image.resizeImageTo(size: iconSize)
+ #if os(iOS)
+ if props.experimentalBakedTintColors {
+ props.icons[index] = icon?.withRenderingMode(.alwaysTemplate)
+ props.iconsRevision += 1
+ } else {
+ props.icons[index] = icon
+ }
+ #else
+ props.icons[index] = icon
+ #endif
}
})
}
diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx
index d8a6e754..c55f7d4e 100644
--- a/packages/react-native-bottom-tabs/src/TabView.tsx
+++ b/packages/react-native-bottom-tabs/src/TabView.tsx
@@ -80,6 +80,13 @@ interface Props {
* Has no effect on iOS 26 and above (Liquid Glass).
*/
tabBarInactiveTintColor?: ColorValue;
+ /**
+ * Enables the experimental iOS 26 Liquid Glass tint color workaround that bakes tab labels into images.
+ * This has many drawbacks, such as affecting icon sizing when labels have different widths, bad positioning of badges, and possibly breaking accessibility features.
+ *
+ * @platform ios
+ */
+ experimental_bakedTintColors?: boolean;
/**
* State for the tab view.
*
@@ -252,6 +259,7 @@ const TabView = ({
tabLabelStyle,
renderBottomAccessoryView,
layoutDirection = 'locale',
+ experimental_bakedTintColors: experimentalBakedTintColors = false,
...props
}: Props) => {
// @ts-ignore
@@ -414,6 +422,7 @@ const TabView = ({
layoutDirection={layoutDirection}
activeTintColor={activeTintColor}
inactiveTintColor={inactiveTintColor}
+ experimentalBakedTintColors={experimentalBakedTintColors}
barTintColor={tabBarStyle?.backgroundColor}
rippleColor={rippleColor}
labeled={labeled}
diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
index 50082c55..4a4e81ad 100644
--- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
+++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts
@@ -53,6 +53,7 @@ export interface TabViewProps extends ViewProps {
rippleColor?: ColorValue;
activeTintColor?: ColorValue;
inactiveTintColor?: ColorValue;
+ experimentalBakedTintColors?: WithDefault;
disablePageAnimations?: boolean;
activeIndicatorColor?: ColorValue;
hapticFeedbackEnabled?: boolean;