new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo · Router · all subjects

native-tabs

40 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Native tabs overview and availability

Native tabs are available in SDK 54 and later and are currently in alpha, meaning the API is subject to change. Unlike other tab layouts in Expo Router, native tabs use the native system tab bar provided by the platform.

File structure for native tabs layout

To create a native tabs layout, create a root layout file at src/app/_layout.tsx that defines the NativeTabs component with NativeTabs.Trigger children for each tab. Individual tab content is defined in separate files like src/app/index.tsx and src/app/settings.tsx. Tab files are not automatically added to the tab bar; they must be explicitly added using NativeTabs.Trigger.

NativeTabs component API (SDK 55 and later)

The NativeTabs component exported from 'expo-router/unstable-native-tabs' contains child components: NativeTabs.Trigger (with child components Label, Icon, and Badge), and NativeTabs.BottomAccessory. Each NativeTabs.Trigger takes a 'name' prop matching a route file, and contains sub-components for customization.

NativeTabs component API (SDK 54)

In SDK 54, NativeTabs is imported from 'expo-router/unstable-native-tabs' along with separate Icon, Label, and Badge components. Each NativeTabs.Trigger takes a 'name' prop, and Icon, Label, and Badge are passed as direct children instead of using compound components like NativeTabs.Trigger.Icon.

Icon component props for SF Symbols and Material Symbols

The Icon component (or NativeTabs.Trigger.Icon in SDK 55+) accepts: 'sf' prop for Apple's SF Symbols on iOS, 'md' prop for Android material symbols, and 'src' prop for custom images. On SDK 56+, 'drawable' prop is used for Android drawables. You can pass an object with {default: ..., selected: ...} to specify different icons for default and selected states.

Icon selected state support by SDK

On Android, distinct selected icons require SDK 56 or later (powered by react-native-screens 4.25+). On SDK 55, the 'src' prop accepts the object form but the selected variant is ignored. The 'drawable' and 'md' props on SDK 55 only accept a string for a single icon used in both states.

Icon rendering mode for custom images

Icon rendering mode is available in SDK 55 and later. The 'renderingMode' prop controls how custom images are rendered on iOS with two options: 'template' (default, applies tint color as a template image for single-color icons) and 'original' (preserves original colors for multi-color or gradient icons). This prop only affects iOS; on Android all image icons render with original colors.

Asset catalog icons with xcasset prop

The 'xcasset' prop (SDK 55+) allows using images from Xcode asset catalog as tab icons. Pass a string for the same icon in both states, or an object with {default: ..., selected: ...} for different icons. Asset catalog icons support the 'renderingMode' prop.

Dynamic color for liquid glass on iOS

Liquid glass on iOS automatically changes colors based on light or dark background. Use DynamicColorIOS or PlatformColor to set icon and text colors dynamically. Pass 'color' in labelStyle prop for text color and 'tintColor' prop on NativeTabs for selected icon color.

Vector icons with react-native-vector-icons

Icons from fonts like react-native-vector-icons can be rendered by passing an image source to the 'src' prop. Install '@react-native-vector-icons/material-design-icons' and '@react-native-vector-icons/get-image', then use getImageSourceSync() computed at module scope. Combine 'src' with 'sf' to use vector icons on Android and SF Symbols on iOS.

Label component for tab bar labels

The Label component (NativeTabs.Trigger.Label in SDK 55+, or Label in SDK 54) displays text in the tab bar item. Pass a string as a child for the label text. Use the 'hidden' prop to hide the label while keeping the tab visible. If no label is provided, the tab bar uses the route name as the label.

Badge component for tab bar badges

The Badge component (NativeTabs.Trigger.Badge in SDK 55+, or Badge in SDK 54) displays a badge mark on the tab for notifications or counts. Pass text as a child (e.g., '9+') to display a count, or use an empty Badge component to show a dot indicator.

Hiding the tab bar with hidden prop

The 'hidden' prop on NativeTabs (SDK 55+) allows hiding the entire tab bar. To hide it for specific screens, use context API to set the hidden prop dynamically. Use useFocusEffect to show/hide the tab bar when navigating to specific screens.

Conditionally hiding individual tabs

Individual tabs can be hidden using the 'hidden' prop on NativeTabs.Trigger. A tab marked as hidden cannot be navigated to in any way. Warning: Dynamically hiding tabs remounts the navigator and resets state; only change tab visibility before the navigator mounts or when not visible.

Dismiss behavior (pop to top on Android)

On Android (SDK 55+), tapping an active tab by default closes all screens in that tab's stack and returns to the root. Disable this by setting 'disablePopToTop' prop on NativeTabs.Trigger.

Scroll to top behavior on Android

On Android (SDK 55+), tapping an active tab while showing the root screen scrolls content to the top by default. Disable this with 'disableScrollToTop' prop on NativeTabs.Trigger.

Disabled tabs with disabled prop

The 'disabled' prop (SDK 56+) on NativeTabs.Trigger prevents native tab selection by tapping. When true, the tab remains visible but tapping doesn't change focus. Note: disabled only suppresses native tap interaction; JavaScript navigation like router.push('/settings') or Link still navigates to the disabled tab. Can be toggled dynamically from inside a screen.

iOS 26 separate search tab role

On iOS, assign 'role="search"' to a NativeTabs.Trigger to display it as a separate search tab. Requires Xcode 26 or higher to compile.

Tab bar search input on iOS with headerSearchBarOptions

To add a search field to the tab bar on iOS, wrap the search tab's screen in a Stack navigator and configure 'headerSearchBarOptions' on the Stack. Use Stack.SearchBar component with placement='automatic' to add search functionality.

Tab bar minimize behavior on iOS

The 'minimizeBehavior' prop on NativeTabs controls tab bar behavior on iOS. Set to 'onScrollDown' to minimize the tab bar when scrolling down. This feature is available on iOS in SDK 55+.

Bottom accessory component for persistent controls

NativeTabs.BottomAccessory renders a floating view above the tab bar, useful for mini players or persistent controls. It appears in two placements: 'regular' (standard position) or 'inline' (compact). Use NativeTabs.BottomAccessory.usePlacement() hook to adapt UI based on current placement. State must be stored outside the accessory component using props, context, or external state management.

Keyboard avoidance on Android with tabBarRespectsIMEInsets

The 'tabBarRespectsIMEInsets' prop (SDK 56+) on NativeTabs makes the tab bar lift above the keyboard on Android instead of being overlaid. Requires Android 11 or later and the app config field 'android.softwareKeyboardLayoutMode' set to 'resize' (Expo default). Changes take effect only after the keyboard closes.

Safe area handling in native tabs

Native tabs automatically handle safe area insets with platform-specific behavior: On Android, screen content is wrapped in SafeAreaView applying the bottom inset for the tab bar. On iOS, the first ScrollView inside a native tabs screen has automatic content inset adjustment enabled.

Disabling automatic content insets

The 'disableAutomaticContentInsets' prop (SDK 55+) on NativeTabs.Trigger disables automatic content inset adjustment. When set to true, you must manage safe area insets manually using SafeAreaView from 'react-native-screens/experimental' with edges={{ bottom: true }}.

Lazy loading strategies in native tabs

All tab screens in native tabs render eagerly when the navigator mounts. Cannot be changed because the native tab bar needs screens available for transitions. To defer expensive content, use useIsFocused to conditionally render (content unmounts on tab switch, losing local state), or useFocusEffect with a state flag to load once on first focus then keep mounted.

Custom web layout for native tabs

Native tabs render platform-specific tab bars on Android and iOS but fall back to a basic implementation on web. Use headless tabs from 'expo-router/ui' to provide custom web layout. Two approaches: create a separate _layout.web.tsx file for web-only layout, or extract tab UI into a component with platform-specific extensions (.tsx and .web.tsx).

Platform-specific layout files for native tabs

Create a _layout.web.tsx file alongside _layout.tsx to provide different layouts per platform. The web file completely replaces the layout on web, allowing each platform to have an entirely different design.

SDK 54 to 55 migration for native tabs

SDK 55 changes the component API: Instead of importing Icon, Label, and Badge separately, use compound components: NativeTabs.Trigger.Icon, NativeTabs.Trigger.Label, and NativeTabs.Trigger.Badge. For Android icons, the 'md' prop is now the recommended way to use Material Symbols.

Native tabs vs JavaScript tabs differences

Native tabs are not a drop-in replacement for JavaScript tabs. Key differences: Use Trigger instead of Screen for adding routes; use React components instead of props objects for UI definition; native tabs use Stacks inside tabs to support headers and screen pushing (JavaScript tabs have a mock stack header). Native tabs are constrained to native platform behavior while JavaScript tabs can be customized more freely.

Tab bar transparency issue on iOS 18 and earlier

The native tab bar becomes transparent on iOS 18 and earlier when scrolling to the end of scrollable content (ScrollView) or when rendering static content. Use 'disableTransparentOnScrollEdge' prop on NativeTabs or NativeTabs.Trigger to disable this behavior. Ensure ScrollView is the first child of the screen component; if wrapped, set 'collapsable' to false on the wrapper.

White background flash on iOS 26 tab switches

White background may flash when switching tabs on iOS 26 because the default theme uses white background. Fix by wrapping the layout in ThemeProvider from 'expo-router' (SDK 56+) or '@react-navigation/native' (SDK 55) with appropriate theme (DarkTheme or DefaultTheme). Alternatively, use the 'contentStyle' prop on NativeTabs.Trigger with a specific background color.

Scroll to top not working when tab is not first child

Scroll to top when tapping an active tab may not work if the ScrollView is not the first child of the screen component. Ensure ScrollView is a direct first child; if wrapped, set 'collapsable' to false on the wrapper component.

Liquid glass header buttons flickering in dark mode iOS 26

Header buttons with liquid glass styling may flicker when switching tabs in dark mode on iOS 26. Caused by default theme not matching system dark mode. Fix by wrapping layout with ThemeProvider from 'expo-router' using DarkTheme or DefaultTheme based on system color scheme.

Android maximum tab limit

On Android, there is a maximum of 5 tabs in the tab bar. This limitation comes from the platform's Material Tabs component.

Cannot measure tab bar height

The tab bar height cannot be reliably measured because tabs move around—sometimes on top of screen on iPad, sometimes on the side on Apple Vision Pro. Layout functions for detailed layout info are in development.

No support for nested native tabs

Native tabs cannot be nested inside other native tabs. JavaScript tabs can still be nested inside native tabs.

FlatList limitations in native tabs

FlatList has limited support in native tabs: scroll-to-top and minimize-on-scroll features aren't supported, and detecting scroll edges may fail, causing the tab bar to appear transparent. Use 'disableTransparentOnScrollEdge' prop to fix transparency issues.

No dynamic tab addition or removal at runtime

Dynamically adding or removing tabs at runtime is not supported. Tabs must be defined statically in the layout file and remain consistent throughout the app lifecycle. This aligns with Apple's Human Interface Guidelines recommending stable tab bar content to help users build mental models. Dynamic changes cause content remounting and state loss.

NativeTabs component for Android and iOS

On Android and iOS, use NativeTabs from 'expo-router/unstable-native-tabs' to render the platform's built-in tab bar. Native tabs provide expected platform behaviors like scroll-to-top on tap, native animations, and native look and feel. Use NativeTabs.Trigger components with NativeTabs.Trigger.Label and NativeTabs.Trigger.Icon child components.

NativeTabs layout example

Example of a native tabs layout: import { NativeTabs } from 'expo-router/unstable-native-tabs'; export default function TabLayout() { return ( <NativeTabs> <NativeTabs.Trigger name="index"> <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label> <NativeTabs.Trigger.Icon src={require('@/assets/images/tabIcons/home.png')} /> </NativeTabs.Trigger> <NativeTabs.Trigger name="feed"> <NativeTabs.Trigger.Label>Feed</NativeTabs.Trigger.Label> <NativeTabs.Trigger.Icon src={require('@/assets/images/tabIcons/feed.png')} /> </NativeTabs.Trigger> <NativeTabs.Trigger name="profile"> <NativeTabs.Trigger.Label>Profile</NativeTabs.Trigger.Label> <NativeTabs.Trigger.Icon src={require('@/assets/images/tabIcons/profile.png')} /> </NativeTabs.Trigger> </NativeTabs> ); }

Give your agent this brain