Documentation Index Fetch the complete documentation index at: https://mintlify.com/eds2002/react-native-screen-transitions/llms.txt
Use this file to discover all available pages before exploring further.
Overlays are persistent components that render above all screens in the stack and can animate in response to navigation changes. Perfect for tab bars, player controls, or custom navigation UI.
Basic Usage
import { useAnimatedStyle , interpolate } from "react-native-reanimated" ;
import Animated from "react-native-reanimated" ;
const TabBar = ({ progress }) => {
const style = useAnimatedStyle (() => ({
transform: [{ translateY: interpolate ( progress . value , [ 0 , 1 ], [ 100 , 0 ]) }],
}));
return (
< Animated.View style = { [ styles . tabBar , style ] } >
{ /* Tab bar content */ }
</ Animated.View >
);
};
< Stack.Screen
name = "Home"
options = { {
overlay: TabBar ,
overlayShown: true ,
} }
/>
Configuration
overlay
overlay
(props: OverlayProps) => React.ReactNode
Function that returns a React Element to display as an overlay. The overlay receives animation values and navigation state as props. options = {{
overlay : ( props ) => < CustomOverlay { ... props } /> ,
}}
overlayShown
Whether to show the overlay. The overlay is shown by default when overlay is provided. Set to false to hide the overlay for specific screens. // Show overlay on home
< Stack.Screen
name = "Home"
options = { {
overlay: TabBar ,
overlayShown: true ,
} }
/>
// Hide overlay on detail
< Stack.Screen
name = "Detail"
options = { {
overlay: TabBar ,
overlayShown: false ,
} }
/>
Overlay Props
Overlay components receive the following props:
focusedRoute
Route object of the currently focused screen in the stack. const TabBar = ({ focusedRoute }) => {
const isHomeActive = focusedRoute . name === "Home" ;
// Highlight appropriate tab
};
focusedIndex
Index of the focused route in the stack (0-based). const TabBar = ({ focusedIndex }) => {
console . log ( `Screen ${ focusedIndex } is active` );
};
routes
All routes currently in the stack. const TabBar = ({ routes }) => {
const stackDepth = routes . length ;
// Adjust UI based on stack depth
};
progress
Stack progress relative to the overlay’s position. Equivalent to useScreenAnimation().stackProgress. Use this for animating the overlay based on navigation state. const TabBar = ({ progress }) => {
const style = useAnimatedStyle (() => ({
opacity: interpolate ( progress . value , [ 0 , 1 ], [ 0 , 1 ]),
transform: [
{ translateY: interpolate ( progress . value , [ 0 , 1 ], [ 100 , 0 ]) }
],
}));
return < Animated.View style = { style } > { /* Content */ } </ Animated.View > ;
};
Custom metadata from the focused screen’s options. // In screen options
< Stack.Screen
name = "Home"
options = { {
meta: { showTabBar: true },
overlay: TabBar ,
} }
/>
// In overlay component
const TabBar = ({ meta }) => {
if ( ! meta ?. showTabBar ) return null ;
return < View > { /* Tab bar */ } </ View > ;
};
navigation
Navigation prop for the overlay. Type depends on which stack you’re using (Blank, Native, or Component). const TabBar = ({ navigation }) => {
return (
< Button
title = "Go to Settings"
onPress = { () => navigation . navigate ( "Settings" ) }
/>
);
};
options
Screen options for the currently focused screen. const TabBar = ({ options }) => {
const hasGestures = options . gestureEnabled ;
// Adjust behavior based on screen config
};
Examples
Animated Tab Bar
import { useAnimatedStyle , interpolate } from "react-native-reanimated" ;
import Animated from "react-native-reanimated" ;
import { StyleSheet } from "react-native" ;
const TabBar = ({ focusedRoute , progress , navigation }) => {
const style = useAnimatedStyle (() => ({
transform: [
{ translateY: interpolate ( progress . value , [ 0 , 1 ], [ 100 , 0 ]) },
],
opacity: interpolate ( progress . value , [ 0 , 1 ], [ 0 , 1 ]),
}));
return (
< Animated.View style = { [ styles . tabBar , style ] } >
< TabButton
title = "Home"
active = { focusedRoute . name === "Home" }
onPress = { () => navigation . navigate ( "Home" ) }
/>
< TabButton
title = "Search"
active = { focusedRoute . name === "Search" }
onPress = { () => navigation . navigate ( "Search" ) }
/>
< TabButton
title = "Profile"
active = { focusedRoute . name === "Profile" }
onPress = { () => navigation . navigate ( "Profile" ) }
/>
</ Animated.View >
);
};
const styles = StyleSheet . create ({
tabBar: {
position: "absolute" ,
bottom: 0 ,
left: 0 ,
right: 0 ,
height: 60 ,
flexDirection: "row" ,
backgroundColor: "white" ,
borderTopWidth: 1 ,
borderTopColor: "#e0e0e0" ,
},
});
// Apply to screens
< Stack.Screen
name = "Home"
options = { {
overlay: TabBar ,
overlayShown: true ,
} }
/>
< Stack.Screen
name = "Search"
options = { {
overlay: TabBar ,
overlayShown: true ,
} }
/>
< Stack.Screen
name = "Detail"
options = { {
overlay: TabBar ,
overlayShown: false , // Hide on detail screen
} }
/>
Music Player Overlay
import { useAnimatedStyle , interpolate } from "react-native-reanimated" ;
import Animated from "react-native-reanimated" ;
import { useSafeAreaInsets } from "react-native-safe-area-context" ;
const MiniPlayer = ({ progress , meta }) => {
const insets = useSafeAreaInsets ();
// Hide player on certain screens
if ( meta ?. hidePlayer ) return null ;
const style = useAnimatedStyle (() => ({
transform: [
{ translateY: interpolate ( progress . value , [ 0 , 1 ], [ 100 , 0 ]) },
],
}));
return (
< Animated.View
style = { [
{
position: "absolute" ,
bottom: insets . bottom + 60 , // Above tab bar
left: 0 ,
right: 0 ,
height: 60 ,
backgroundColor: "#1DB954" ,
flexDirection: "row" ,
alignItems: "center" ,
paddingHorizontal: 16 ,
},
style ,
] }
>
< Image source = { albumArt } style = { { width: 40 , height: 40 } } />
< View style = { { flex: 1 , marginLeft: 12 } } >
< Text style = { { color: "white" , fontWeight: "bold" } } > Song Title </ Text >
< Text style = { { color: "#b3b3b3" } } > Artist Name </ Text >
</ View >
< PlayPauseButton />
</ Animated.View >
);
};
// Apply to screens
< Stack.Screen
name = "Home"
options = { {
overlay: MiniPlayer ,
overlayShown: true ,
} }
/>
< Stack.Screen
name = "Player"
options = { {
overlay: MiniPlayer ,
overlayShown: false , // Hide on full player
meta: { hidePlayer: true },
} }
/>
const ConditionalOverlay = ({ meta , progress }) => {
// Different overlays for different screen types
if ( meta ?. overlayType === "tabBar" ) {
return < TabBar progress = { progress } /> ;
}
if ( meta ?. overlayType === "toolbar" ) {
return < Toolbar progress = { progress } /> ;
}
return null ;
};
// Configure per screen
< Stack.Screen
name = "Home"
options = { {
overlay: ConditionalOverlay ,
meta: { overlayType: "tabBar" },
} }
/>
< Stack.Screen
name = "Editor"
options = { {
overlay: ConditionalOverlay ,
meta: { overlayType: "toolbar" },
} }
/>
< Stack.Screen
name = "Detail"
options = { {
overlay: ConditionalOverlay ,
// No meta.overlayType - renders nothing
} }
/>
Stack Depth Indicator
const StackDepth = ({ routes , focusedIndex }) => {
return (
< View
style = { {
position: "absolute" ,
top: 50 ,
right: 20 ,
backgroundColor: "rgba(0,0,0,0.7)" ,
padding: 8 ,
borderRadius: 4 ,
} }
>
< Text style = { { color: "white" } } >
{ focusedIndex + 1 } / { routes . length }
</ Text >
</ View >
);
};
Using useScreenAnimation in Overlays
You can also use useScreenAnimation() inside overlay components for more complex animations:
import { useScreenAnimation } from "react-native-screen-transitions" ;
import { useAnimatedStyle , interpolate } from "react-native-reanimated" ;
import Animated from "react-native-reanimated" ;
const AdvancedOverlay = () => {
const animation = useScreenAnimation ();
const style = useAnimatedStyle (() => {
const progress = animation . value . current . progress ;
const stackProgress = animation . stackProgress . value ;
return {
opacity: interpolate ( stackProgress , [ 0 , 1 , 2 ], [ 0 , 1 , 0.5 ]),
transform: [
{
scale: interpolate ( progress , [ 0 , 1 ], [ 0.9 , 1 ]),
},
{
translateY: interpolate ( stackProgress , [ 0 , 1 ], [ 50 , 0 ]),
},
],
};
});
return (
< Animated.View style = { [ styles . overlay , style ] } >
{ /* Overlay content */ }
</ Animated.View >
);
};
Best Practices
Use progress for Animations The progress prop is optimized for overlay animations. Use it with useAnimatedStyle to create smooth, performant animations.
Control Visibility with overlayShown Instead of conditionally rendering overlays, use overlayShown to control visibility. This maintains overlay state across screen transitions.
Pass Data with meta Use the meta option to pass custom data to overlays. This is more type-safe and maintainable than global state for overlay configuration.
Position Absolutely Overlays should use absolute positioning to float above screen content. Use safe area insets for proper spacing on notched devices.
Common Patterns
Hiding Overlay on Specific Screens
// Method 1: Using overlayShown
< Stack.Screen
name = "Fullscreen"
options = { {
overlay: TabBar ,
overlayShown: false ,
} }
/>
// Method 2: Using meta
< Stack.Screen
name = "Fullscreen"
options = { {
overlay : ({ meta }) => {
if ( meta ?. hideOverlay ) return null ;
return < TabBar /> ;
},
meta: { hideOverlay: true },
} }
/>
Different Overlays for Different Screens
const HomeOverlay = ( props ) => < TabBar { ... props } /> ;
const EditorOverlay = ( props ) => < Toolbar { ... props } /> ;
< Stack.Screen
name = "Home"
options = { { overlay: HomeOverlay } }
/>
< Stack.Screen
name = "Editor"
options = { { overlay: EditorOverlay } }
/>
Animating Based on Route
const SmartOverlay = ({ focusedRoute , progress }) => {
const style = useAnimatedStyle (() => {
// Different animations for different routes
const shouldHide = focusedRoute . name === "Detail" ;
return {
transform: [
{
translateY: interpolate (
progress . value ,
[ 0 , 1 ],
[ shouldHide ? 100 : 0 , 0 ]
),
},
],
};
});
return < Animated.View style = { style } > { /* Content */ } </ Animated.View > ;
};
TypeScript
import type { OverlayProps } from "react-native-screen-transitions" ;
import type { BlankStackNavigationProp } from "react-native-screen-transitions/blank-stack" ;
// Type your overlay component
const TabBar = ( props : OverlayProps < BlankStackNavigationProp >) => {
const { focusedRoute , progress , navigation } = props ;
// Fully typed props
};
// With custom meta
interface CustomMeta {
showTabBar : boolean ;
theme : "light" | "dark" ;
}
const TabBar = ( props : OverlayProps ) => {
const meta = props . meta as CustomMeta | undefined ;
// Type-safe meta access
};