Bottom tab navigator following iOS design guidelines for React Navigation
—
Screen-level configuration options including animations, lazy loading, headers, and behavior control for optimizing performance and user experience in bottom tab navigation.
Control when and how screens are loaded and rendered.
interface ScreenLoadingOptions {
/**
* Whether this screen should render the first time it's accessed. Defaults to `true`.
* Set it to `false` if you want to render the screen on initial render.
*/
lazy?: boolean;
/**
* Whether inactive screens should be suspended from re-rendering. Defaults to `false`.
* Defaults to `true` when `enableFreeze()` is run at the top of the application.
* Requires `react-native-screens` version >=3.16.0.
* Only supported on iOS and Android.
*/
freezeOnBlur?: boolean;
}Usage Examples:
<Tab.Screen
name="ExpensiveScreen"
component={ExpensiveScreen}
options={{
lazy: true, // Only load when first accessed
freezeOnBlur: true, // Suspend when not active
}}
/>
// Load immediately for important screens
<Tab.Screen
name="Dashboard"
component={DashboardScreen}
options={{
lazy: false, // Load on app start
}}
/>Configure how screens behave during navigation and focus changes.
interface ScreenNavigationBehavior {
/**
* Whether any nested stack should be popped to top when navigating away from the tab.
* Defaults to `false`.
*/
popToTopOnBlur?: boolean;
}Usage Examples:
<Tab.Screen
name="UserProfile"
component={UserProfileNavigator}
options={{
popToTopOnBlur: true, // Reset nested navigation when leaving tab
}}
/>Configure custom animations for screen transitions when switching tabs.
interface ScreenAnimationOptions {
/**
* How the screen should animate when switching tabs.
* Supported values:
* - 'none': don't animate the screen (default)
* - 'fade': cross-fade the screens.
* - 'shift': screens slightly shift to left/right.
*/
animation?: TabAnimationName;
/**
* Function which specifies interpolated styles for bottom-tab scenes.
*/
sceneStyleInterpolator?: BottomTabSceneStyleInterpolator;
/**
* Object which specifies the animation type (timing or spring) and their options.
*/
transitionSpec?: TransitionSpec;
}
type TabAnimationName = 'none' | 'fade' | 'shift';
type BottomTabSceneStyleInterpolator = (
props: BottomTabSceneInterpolationProps
) => BottomTabSceneInterpolatedStyle;
type TransitionSpec =
| {
animation: 'timing';
config: Omit<
Animated.TimingAnimationConfig,
'toValue' | keyof Animated.AnimationConfig
>;
}
| {
animation: 'spring';
config: Omit<
Animated.SpringAnimationConfig,
'toValue' | keyof Animated.AnimationConfig
>;
};Usage Examples:
import { SceneStyleInterpolators, TransitionSpecs } from '@react-navigation/bottom-tabs';
<Tab.Screen
name="AnimatedScreen"
component={AnimatedScreen}
options={{
animation: 'fade',
sceneStyleInterpolator: SceneStyleInterpolators.forFade,
transitionSpec: TransitionSpecs.FadeSpec,
}}
/>
// Custom animation
<Tab.Screen
name="CustomAnimated"
component={CustomScreen}
options={{
animation: 'shift',
sceneStyleInterpolator: ({ current }) => ({
sceneStyle: {
opacity: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0, 1, 0],
}),
transform: [
{
scale: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0.85, 1, 0.85],
}),
},
],
},
}),
transitionSpec: {
animation: 'spring',
config: {
stiffness: 1000,
damping: 500,
mass: 3,
},
},
}}
/>Control the visual appearance of individual screens.
interface ScreenStylingOptions {
/**
* Style object for the component wrapping the screen content.
*/
sceneStyle?: StyleProp<ViewStyle>;
}Usage Examples:
<Tab.Screen
name="ThemedScreen"
component={ThemedScreen}
options={{
sceneStyle: {
backgroundColor: '#f5f5f5',
paddingTop: 20,
},
}}
/>Configure headers for individual screens within the tab navigator.
interface ScreenHeaderOptions {
/**
* Function that returns a React Element to display as a header.
*/
header?: (props: BottomTabHeaderProps) => React.ReactNode;
/**
* Whether to show the header. Setting this to `false` hides the header.
* Defaults to `true`.
*/
headerShown?: boolean;
/**
* Title text for the screen.
*/
title?: string;
}
interface BottomTabHeaderProps {
/**
* Layout of the screen.
*/
layout: Layout;
/**
* Options for the current screen.
*/
options: BottomTabNavigationOptions;
/**
* Route object for the current screen.
*/
route: RouteProp<ParamListBase>;
/**
* Navigation prop for the header.
*/
navigation: BottomTabNavigationProp<ParamListBase>;
}Usage Examples:
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
title: 'User Profile',
headerShown: true,
header: ({ navigation, route, options }) => (
<CustomHeader
title={options.title}
onBackPress={() => navigation.goBack()}
/>
),
}}
/>
// Hide header for specific screen
<Tab.Screen
name="FullScreen"
component={FullScreenScreen}
options={{
headerShown: false,
}}
/>For custom scene style interpolators, you have access to animation progress values.
interface BottomTabSceneInterpolationProps {
/**
* Values for the current screen.
*/
current: {
/**
* Animated value for the current screen:
* - -1 if the index is lower than active tab,
* - 0 if they're active,
* - 1 if the index is higher than active tab
*/
progress: Animated.Value;
};
}
interface BottomTabSceneInterpolatedStyle {
/**
* Interpolated style for the view representing the scene containing screen content.
*/
sceneStyle: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
}Usage Examples:
const customInterpolator = ({ current }) => ({
sceneStyle: {
opacity: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0, 1, 0],
}),
transform: [
{
translateX: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [-100, 0, 100],
}),
},
{
rotateY: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: ['-45deg', '0deg', '45deg'],
}),
},
],
},
});Handle navigation events at the screen level.
interface BottomTabNavigationListeners {
tabPress?: (e: EventArg<'tabPress', true, undefined>) => void;
tabLongPress?: (e: EventArg<'tabLongPress', false, undefined>) => void;
transitionStart?: (e: EventArg<'transitionStart', false, undefined>) => void;
transitionEnd?: (e: EventArg<'transitionEnd', false, undefined>) => void;
focus?: (e: EventArg<'focus', false, undefined>) => void;
blur?: (e: EventArg<'blur', false, undefined>) => void;
}Usage Examples:
<Tab.Screen
name="Analytics"
component={AnalyticsScreen}
listeners={{
tabPress: (e) => {
// Track tab press analytics
analytics.track('tab_pressed', { tab: 'Analytics' });
},
focus: () => {
// Refresh data when screen comes into focus
refreshAnalyticsData();
},
blur: () => {
// Cleanup or save state when leaving screen
saveCurrentState();
},
}}
/>Configure screen options based on navigation state, route parameters, or theme.
interface BottomTabOptionsArgs<
ParamList extends ParamListBase,
RouteName extends keyof ParamList = keyof ParamList,
NavigatorID extends string | undefined = undefined
> extends BottomTabScreenProps<ParamList, RouteName, NavigatorID> {
theme: Theme;
}
type ScreenOptionsFunction = (
args: BottomTabOptionsArgs
) => BottomTabNavigationOptions;Usage Examples:
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={({ route, navigation, theme }) => ({
title: route.params?.section ? `${route.params.section} Settings` : 'Settings',
sceneStyle: {
backgroundColor: theme.colors.background,
},
tabBarBadge: route.params?.hasUpdates ? '!' : undefined,
headerShown: route.params?.hideHeader !== true,
})}
/>Install with Tessl CLI
npx tessl i tessl/npm-react-navigation--bottom-tabs