Bottom tab navigator following iOS design guidelines for React Navigation
—
Built-in animation presets and custom transition configurations for smooth tab switching experiences with full control over timing, easing, and visual effects.
Pre-built interpolation functions for common tab transition animations.
/**
* Scene style interpolators for tab transitions
*/
const SceneStyleInterpolators: {
/**
* Simple cross-fade animation between tabs
*/
forFade: BottomTabSceneStyleInterpolator;
/**
* Animation where screens slightly shift to left/right during transitions
*/
forShift: BottomTabSceneStyleInterpolator;
};
/**
* Function type for scene style interpolation
*/
type BottomTabSceneStyleInterpolator = (
props: BottomTabSceneInterpolationProps
) => BottomTabSceneInterpolatedStyle;
interface BottomTabSceneInterpolationProps {
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 containing screen content
*/
sceneStyle: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
}Usage Examples:
import { SceneStyleInterpolators } from "@react-navigation/bottom-tabs";
<Tab.Screen
name="FadeScreen"
component={FadeScreen}
options={{
animation: 'fade',
sceneStyleInterpolator: SceneStyleInterpolators.forFade,
}}
/>
<Tab.Screen
name="ShiftScreen"
component={ShiftScreen}
options={{
animation: 'shift',
sceneStyleInterpolator: SceneStyleInterpolators.forShift,
}}
/>Complete animation presets combining timing specifications with style interpolators.
/**
* Pre-configured transition presets for tab animations
*/
const TransitionPresets: {
/**
* Fade transition with timing configuration
*/
FadeTransition: BottomTabTransitionPreset;
/**
* Shift transition with timing configuration
*/
ShiftTransition: BottomTabTransitionPreset;
};
interface BottomTabTransitionPreset {
/**
* Whether transition animations should be enabled when switching tabs
*/
animationEnabled?: boolean;
/**
* Function which specifies interpolated styles for bottom-tab scenes
*/
sceneStyleInterpolator?: BottomTabSceneStyleInterpolator;
/**
* Object which specifies the animation type (timing or spring) and options
*/
transitionSpec?: TransitionSpec;
}Usage Examples:
import { TransitionPresets } from "@react-navigation/bottom-tabs";
<Tab.Navigator
screenOptions={{
...TransitionPresets.FadeTransition,
}}
>
{/* All screens will use fade transition */}
</Tab.Navigator>
<Tab.Screen
name="SpecialScreen"
component={SpecialScreen}
options={{
...TransitionPresets.ShiftTransition,
// Override specific properties
animationEnabled: true,
}}
/>Timing and spring animation configurations for precise control over transition behavior.
/**
* Animation timing specifications
*/
const TransitionSpecs: {
/**
* Timing spec for fade animations (150ms linear easing)
*/
FadeSpec: TransitionSpec;
/**
* Timing spec for shift animations (150ms ease-in-out)
*/
ShiftSpec: TransitionSpec;
};
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 { TransitionSpecs } from "@react-navigation/bottom-tabs";
<Tab.Screen
name="TimedScreen"
component={TimedScreen}
options={{
transitionSpec: TransitionSpecs.FadeSpec,
animation: 'fade',
}}
/>
// Custom timing specification
<Tab.Screen
name="CustomTiming"
component={CustomScreen}
options={{
transitionSpec: {
animation: 'timing',
config: {
duration: 300,
easing: Easing.bezier(0.4, 0, 0.2, 1),
},
},
}}
/>
// Spring animation specification
<Tab.Screen
name="SpringScreen"
component={SpringScreen}
options={{
transitionSpec: {
animation: 'spring',
config: {
stiffness: 1000,
damping: 500,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
},
},
}}
/>Create custom animation effects by implementing your own scene style interpolators.
/**
* Custom scene style interpolator implementation
*/
type CustomSceneStyleInterpolator = (
props: BottomTabSceneInterpolationProps
) => BottomTabSceneInterpolatedStyle;Usage Examples:
// Custom scale and rotate interpolator
const scaleRotateInterpolator = ({ 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],
}),
},
{
rotateY: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: ['-45deg', '0deg', '45deg'],
}),
},
],
},
});
// Custom slide and blur effect
const slideBlurInterpolator = ({ current }) => ({
sceneStyle: {
opacity: current.progress.interpolate({
inputRange: [-1, -0.5, 0, 0.5, 1],
outputRange: [0, 0.5, 1, 0.5, 0],
}),
transform: [
{
translateX: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [-200, 0, 200],
}),
},
{
scale: current.progress.interpolate({
inputRange: [-1, -0.2, 0, 0.2, 1],
outputRange: [0.8, 0.95, 1, 0.95, 0.8],
}),
},
],
},
});
<Tab.Screen
name="CustomAnimated"
component={CustomScreen}
options={{
sceneStyleInterpolator: scaleRotateInterpolator,
transitionSpec: {
animation: 'spring',
config: {
stiffness: 100,
damping: 15,
},
},
}}
/>Built-in animation names for common transition effects.
/**
* Built-in animation names for tab transitions
*/
type TabAnimationName = 'none' | 'fade' | 'shift';Usage Examples:
<Tab.Navigator
screenOptions={{
animation: 'fade', // Apply fade animation to all screens
}}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen
name="Special"
component={SpecialScreen}
options={{
animation: 'shift', // Override with shift animation
}}
/>
<Tab.Screen
name="Static"
component={StaticScreen}
options={{
animation: 'none', // No animation for this screen
}}
/>
</Tab.Navigator>Configure tab bar visibility animations when the keyboard appears or disappears.
/**
* Animation configuration for tab bar visibility changes
*/
interface TabBarVisibilityAnimationConfig {
show?: TabBarVisibilityAnimation;
hide?: TabBarVisibilityAnimation;
}
type TabBarVisibilityAnimation =
| TimingKeyboardAnimationConfig
| SpringKeyboardAnimationConfig;
interface TimingKeyboardAnimationConfig {
animation: 'timing';
config?: Omit<
Partial<Animated.TimingAnimationConfig>,
'toValue' | 'useNativeDriver'
>;
}
interface SpringKeyboardAnimationConfig {
animation: 'spring';
config?: Omit<
Partial<Animated.SpringAnimationConfig>,
'toValue' | 'useNativeDriver'
>;
}Usage Examples:
<Tab.Navigator
screenOptions={{
tabBarHideOnKeyboard: true,
tabBarVisibilityAnimationConfig: {
show: {
animation: 'spring',
config: {
damping: 15,
stiffness: 150,
mass: 1,
},
},
hide: {
animation: 'timing',
config: {
duration: 200,
easing: Easing.out(Easing.cubic),
},
},
},
}}
>
{/* screens */}
</Tab.Navigator>Optimize animations for better performance and smoother transitions.
/**
* Animation configuration options for performance optimization
*/
interface AnimationPerformanceOptions {
/**
* Whether to use native driver for animations (automatically determined by platform)
*/
useNativeDriver?: boolean;
/**
* Whether to enable GPU acceleration for transforms
*/
enableGPUAcceleration?: boolean;
}Usage Examples:
// Optimized custom interpolator
const optimizedInterpolator = ({ current }) => ({
sceneStyle: {
// Use transforms instead of changing layout properties
transform: [
{
translateX: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [-50, 0, 50],
}),
},
],
// Avoid changing opacity if not necessary for better performance
opacity: current.progress.interpolate({
inputRange: [-1, -0.01, 0, 0.01, 1],
outputRange: [0, 0, 1, 0, 0],
}),
},
});
// Performance-optimized timing config
const performantTransitionSpec = {
animation: 'timing',
config: {
duration: 150,
easing: Easing.out(Easing.cubic),
// These properties are automatically handled by React Navigation
useNativeDriver: true,
},
};Monitor and control animation states programmatically.
/**
* Animation event handlers
*/
interface AnimationEventHandlers {
/**
* Called when transition animation starts
*/
onTransitionStart?: () => void;
/**
* Called when transition animation ends
*/
onTransitionEnd?: () => void;
}Usage Examples:
function AnimatedScreen({ navigation }) {
React.useEffect(() => {
const startListener = navigation.addListener('transitionStart', () => {
console.log('Animation started');
// Pause expensive operations during animation
pauseBackgroundTasks();
});
const endListener = navigation.addListener('transitionEnd', () => {
console.log('Animation ended');
// Resume operations after animation
resumeBackgroundTasks();
});
return () => {
startListener();
endListener();
};
}, [navigation]);
return <View>{/* screen content */}</View>;
}Complete example combining multiple animation features for a rich user experience.
const advancedAnimationConfig = {
animation: 'fade',
sceneStyleInterpolator: ({ current }) => ({
sceneStyle: {
opacity: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0, 1, 0],
}),
transform: [
{
scale: current.progress.interpolate({
inputRange: [-1, -0.5, 0, 0.5, 1],
outputRange: [0.9, 0.95, 1, 0.95, 0.9],
}),
},
{
translateY: current.progress.interpolate({
inputRange: [-1, 0, 1],
outputRange: [50, 0, -50],
}),
},
],
},
}),
transitionSpec: {
animation: 'spring',
config: {
stiffness: 300,
damping: 25,
mass: 1,
},
},
};
<Tab.Screen
name="AdvancedAnimated"
component={AdvancedScreen}
options={advancedAnimationConfig}
/>Install with Tessl CLI
npx tessl i tessl/npm-react-navigation--bottom-tabs