30 KiB
Native Stack Navigator
[!CAUTION] NativeStack has been moved from react-native-screens/native-stack to @react-navigation/native since version v6. With react-native-screens v4 native stack v5 (react-native-screens/native-stack) is deprecated and marked for removal in the upcoming minor release, react-native-screens v4 will support only @react-navigation/native-stack v7.
Provides a way for your app to transition between screens where each new screen is placed on top of a stack.
By default the stack navigator is configured to have the familiar iOS and Android look & feel: new screens slide in from the right on iOS, fade in and scale from center on Android. On iOS, the stack navigator can also be configured to a modal style where screens slide in from the bottom.
This navigator uses native navigation primitives (UINavigationController on iOS and Fragment on Android) for navigation under the hood. The main difference from React Navigation's JS-based stack navigator is that the JS-based navigator re-implements animations and gestures while the native stack navigator relies on the platform primitives for animations and gestures. You should use this navigator if you want native feeling and performance for navigation and don't need much customization, as the customization options of this navigator are limited.
npm install react-native-screens @react-navigation/native
Disabling react-native-screens
If, for whatever reason, you'd like to disable native screens support and use plain React Native Views add the following code in your entry file (e.g. App.js):
import { enableScreens } from 'react-native-screens';
enableScreens(false);
API Definition
To use this navigator, import it from react-native-screens/native-stack:
import { createNativeStackNavigator } from 'react-native-screens/native-stack';
const Stack = createNativeStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Notifications" component={Notifications} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
);
}
Props
The Stack.Navigator component accepts following props:
initialRouteName
The name of the route to render on the first load of the navigator.
screenOptions
Default options to use for the screens in the navigator.
Options
The options prop can be used to configure individual screens inside the navigator. Supported options are:
backButtonInCustomView
Boolean indicating whether to hide the back button while using headerLeft function.
contentStyle
Style object for the scene content.
customAnimationOnSwipe (iOS only)
Boolean indicating that swipe dismissal should trigger animation provided by stackAnimation. Defaults to false.
direction
String that applies rtl or ltr form to the stack. On Android, you have to add android:supportsRtl="true" in the manifest of your app to enable rtl. On Android, if you set the above flag in the manifest, the orientation changes without the need to do it programmatically if the phone has rtl direction enabled. On iOS, the direction defaults to ltr, and only way to change it is via this prop.
disableBackButtonMenu (iOS only)
Boolean indicating whether to show the menu on longPress of iOS >= 14 back button.
backButtonDisplayMode (iOS only)
Enum value indicating display mode of back button. It is used only when none of: backTitleFontFamily, backTitleFontSize, disableBackButtonMenu and backTitleVisible=false is set. The backTitleVisible forces backButtonDisplayMode: minimal and omits other values. Read more #2800. The other props, under the hood, customize backButtonItem which overrides backButtonDisplayMode. Read more #2123.
Possible options:
default– show given back button previous controller title, system generic or just icon based on available spacegeneric– show given system generic or just icon based on available spaceminimal– show just an icon
fullScreenSwipeEnabled (iOS only)
Boolean indicating whether the swipe gesture should work on whole screen. Swiping with this option results in the same transition animation as simple_push by default. It can be changed to other custom animations with customAnimationOnSwipe prop, but default iOS swipe animation is not achievable due to usage of custom recognizer. Defaults to false.
fullScreenSwipeShadowEnabled (iOS only)
Boolean indicating whether the full screen dismiss gesture has shadow under view during transition. The gesture uses custom transition and thus
doesn't have a shadow by default. When enabled, a custom shadow view is added during the transition which tries to mimic the
default iOS shadow. Defaults to true.
gestureEnabled (iOS only)
Whether you can use gestures to dismiss this screen. Defaults to true.
gestureResponseDistance (iOS only)
Use it to restrict the distance from the edges of screen in which the gesture should be recognized. To be used alongside fullScreenSwipeEnabled. The responsive area is covered with 4 values: start, end, top, bottom. Example usage:
gestureResponseDistance: {
start: 200,
end: 250,
top: 100,
bottom: 150,
}
headerBackTitle
Title string used by the back button on iOS. Defaults to the previous scene's headerTitle when not set or set to whitespace only value.
headerBackTitleStyle
Style object for header back title. Supported properties:
fontFamilyfontSize
headerBackTitleVisible (iOS only)
Whether the back button title should be visible. Defaults to true.
When set to false it works as a "kill switch": it enforces backButtonDisplayMode=minimal and ignores backButtonDisplayMode, backTitleFontSize, backTitleFontFamily, disableBackButtonMenu, and backTitle works only for back button menu.
headerCenter
Function which returns a React Element to display in the center of the header.
headerHideBackButton
Boolean indicating whether to hide the back button in the header.
headerHideShadow
Boolean indicating whether to hide the elevation shadow on the header.
headerLargeStyle (iOS only)
Style object for the large header. Supported properties:
backgroundColor
headerLargeTitle (iOS only)
Boolean used to set a native property to prefer a large title header (like in iOS setting).
For the large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as ScrollView or FlatList. If the scrollable area doesn't fill the screen, the large title won't collapse on scroll.
headerLargeTitleHideShadow (iOS only)
Boolean that allows for disabling drop shadow under navigation header when the edge of any scrollable content reaches the matching edge of the navigation bar.
headerLargeTitleStyle (iOS only)
Style object for header large title. Supported properties:
fontFamilyfontSizecolor
headerLeft
Function which returns a React Element to display on the left side of the header. For now, on Android, using it will cause the title to also disappear.
headerRight
Function which returns a React Element to display on the right side of the header.
headerShown
Whether to show or hide the header for the screen. The header is shown by default. Setting this to false hides the header.
headerStyle
Style object for the header. Supported properties:
backgroundColorblurEffect(iOS only).
headerTintColor
Tint color for the header. Changes the color of the back button and title.
headerTitle
String to be used by the header as title string. Defaults to scene title.
headerTitleStyle
Style object for header title. Supported properties:
fontFamilyfontSizefontWeightcolor
headerTopInsetEnabled (Android only)
A Boolean to that lets you opt out of insetting the header. You may want to * set this to false if you use an opaque status bar. Defaults to true. Insets are always applied on iOS because the header cannot be opaque.
headerTranslucent
Boolean indicating whether the navigation bar is translucent.
hideKeyboardOnSwipe (iOS only)
Whether the keyboard should hide when swiping to the previous screen. Defaults to false.
homeIndicatorHidden (iOS only)
Whether the home indicator should be hidden on this screen. Defaults to false.
nativeBackButtonDismissalEnabled (Android only)
Boolean indicating whether, when the Android default back button is clicked, the pop action should be performed on the native side or on the JS side to be able to prevent it.
Unfortunately the same behavior is not available on iOS since the behavior of native back button cannot be changed there.
Defaults to false.
navigationBarColor (Android only)
Sets the navigation bar color. Defaults to initial status bar color.
navigationBarHidden (Android only)
Sets the visibility of the navigation bar. Defaults to false.
replaceAnimation
How should the screen replacing another screen animate. The following values are currently supported:
push– the new screen will perform push animation.pop– the new screen will perform pop animation.
Defaults to pop.
sheetAllowedDetents
Describes heights where a sheet can rest.
Works only when stackPresentation is set to formSheet.
Heights should be described as fraction (a number from [0, 1] interval) of screen height / maximum detent height.
There is also possibility to specify [-1] literal array with single element, which intets to set the sheet height
to the height of its contents.
Please note that the array must be sorted in ascending order.
There are also legacy & deprecated options available:
- 'medium' - corresponds to
[0.5]detent value, around half of the screen height, - 'large' - corresponds to
[1.0]detent value, maximum height, - 'all' - corresponds to
[0.5, 1.0]value, the name is deceiving due to compatibility reasons.
Defaults to [1.0] literal.
sheetElevation (Android only)
Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet.
Not dynamic - changing it after the component is rendered won't have an effect.
Defaults to 24.
sheetExpandsWhenScrolledToEdge (iOS only)
Whether the sheet should expand to larger detent when scrolling.
Works only when stackPresentation is set to formSheet.
Defaults to true.
sheetCornerRadius
The corner radius that the sheet will try to render with.
Works only when stackPresentation is set to formSheet.
If set to non-negative value it will try to render sheet with provided radius, else it will apply system default.
Defaults to system default.
sheetInitialDetent
Index of the detent the sheet should expand to after being opened.
Works only when presentation is set to formSheet.
Defaults to 0 - which represents first detent in the detents array.
sheetGrabberVisible (iOS only)
Boolean indicating whether the sheet shows a grabber at the top.
Works only when stackPresentation is set to formSheet.
Defaults to false.
sheetLargestUndimmedDetent
The largest sheet detent for which a view underneath won't be dimmed.
Works only when stackPresentation is set to formSheet.
This prop can be set to an number, which indicates index of detent in sheetAllowedDetents array for which
there won't be a dimming view beneath the sheet.
Additionaly there are following options available:
none- there will be dimming view for all detents levels,largest- there won't be a dimming view for any detent level.
There also legacy & deprecated prop values available: medium, large (don't confuse with largest), all, which work in tandem with
corresponding legacy prop values for sheetAllowedDetents prop.
Defaults to none, indicating that the dimming view should be always present.
stackAnimation
How the given screen should appear/disappear when pushed or popped at the top of the stack. Possible values:
default- uses a platform default animationfade- fades screen in or out.fade_from_bottom– performs a fade from bottom animationflip– flips the screen, requires stackPresentation:modal(iOS only)simple_push– performs a default animation, but without native header transition (iOS only)slide_from_bottom– performs a slide from bottom animationslide_from_right- slide in the new screen from right to left (Android only, resolves to default transition on iOS)slide_from_left- slide in the new screen from left to right"ios_from_right"- iOS like slide in animation. pushes in the new screen from right to left (Android only, resolves to default transition on iOS)"ios_from_left"- iOS like slide in animation. pushes in the new screen from left to right (Android only, resolves to default transition on iOS)none- the screen appears/disappears without an animation.
Defaults to default.
stackPresentation
How the screen should be presented. Possible values:
push- The new screen will be pushed onto a stack. The default animation on iOS is to slide from the side. The animation on Android may vary depending on the OS version and theme.modal- The new screen will be presented modally. In addition, this allows for a nested stack to be rendered inside such screens.transparentModal- The new screen will be presented modally. In addition, the second to last screen will remain attached to the stack container such that if the top screen is translucent, the content below can still be seen. If"modal"is used instead, the below screen gets removed as soon as the transition ends.containedModal– will use "UIModalPresentationCurrentContext" modal style on iOS and will fallback to"modal"on Android.containedTransparentModal– will use "UIModalPresentationOverCurrentContext" modal style on iOS and will fallback to"transparentModal"on Android.fullScreenModal– will use "UIModalPresentationFullScreen" modal style on iOS and will fallback to"modal"on Android.formSheet– will use "UIModalPresentationFormSheet" modal style on iOS and "BottomSheetBehavior" on Android.pageSheet– will use "UIModalPresentationPageSheet" modal style on iOS and will fallback to"modal"on Android.
Defaults to push.
Using containedModal and containedTransparentModal with other types of modals in one native stack navigator is not recommended and can result in a freeze or a crash of the application.
swipeDirection (iOS only)
Sets the direction in which you should swipe to dismiss the screen. The following values are supported:
vertical– dismiss screen verticallyhorizontal– dismiss screen horizontally (default)
When using vertical option, options fullScreenSwipeEnabled: true, customAnimationOnSwipe: true and stackAnimation: 'slide_from_bottom' are set by default.
title
A string that can be used as a fallback for headerTitle.
transitionDuration (iOS only)
Changes the duration (in milliseconds) of slide_from_bottom, fade_from_bottom, fade and simple_push transitions on iOS. Defaults to 500.
The duration of default and flip transitions isn't customizable.
freezeOnBlur
Whether inactive screens should be suspended from re-rendering.
Defaults to false. When enableFreeze() is run at the top of the application defaults to true.
useTransitionProgress
Hook providing context value of transition progress of the current screen to be used with react-native Animated. It consists of 2 values:
progress-Animated.Valuebetween0.0and1.0with the progress of the current transition.closing-Animated.Valueof1or0indicating if the current screen is being navigated into or from.goingForward-Animated.Valueof1or0indicating if the current transition is pushing or removing screens.
import { Animated } from 'react-native';
import { useTransitionProgress } from 'react-native-screens';
function Home() {
const { progress } = useTransitionProgress();
const opacity = progress.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1.0, 0.0, 1.0],
extrapolate: 'clamp',
});
return (
<Animated.View
style={{ opacity, height: 50, width: '100%', backgroundColor: 'green' }}
/>
);
}
useReanimatedTransitionProgress
A callback called every frame during the transition of screens to be used with react-native-reanimated version 2.x. It consists of 2 shared values:
progress- between0.0and1.0with the progress of the current transition.closing-1or0indicating if the current screen is being navigated into or from.goingForward-1or0indicating if the current transition is pushing or removing screens.
In order to use it, you need to have react-native-reanimated version 2.x installed in your project and wrap your code with ReanimatedScreenProvider, like this:
import { ReanimatedScreenProvider } from 'react-native-screens/reanimated';
export default function App() {
return (
<ReanimatedScreenProvider>
<YourApp />
</ReanimatedScreenProvider>
);
}
Then you can use useReanimatedTransitionProgress to get the shared values:
import { useReanimatedTransitionProgress } from 'react-native-screens/reanimated';
import Animated, {
useAnimatedStyle,
useDerivedValue,
} from 'react-native-reanimated';
function Home() {
const reaProgress = useReanimatedTransitionProgress();
const sv = useDerivedValue(
() =>
(reaProgress.progress.value < 0.5
? reaProgress.progress.value * 50
: (1 - reaProgress.progress.value) * 50) + 50,
);
const reaStyle = useAnimatedStyle(() => {
return {
width: sv.value,
height: sv.value,
backgroundColor: 'blue',
};
});
return <Animated.View style={reaStyle} />;
}
Status bar and orientation managment
With native-stack, the status bar and screen orientation can be managed by UIViewController on iOS. On Android, the status bar and screen orientation can be managed by FragmentActivity. On iOS, it requires:
- For status bar managment: enabling (or deleting)
View controller-based status bar appearancein your Info.plist file (it disables the option to use React Native'sStatusBarcomponent). - For both status bar and orientation managment: adding
#import <RNScreens/UIViewController+RNScreens.h>in your project'sAppDelegate.m(you can see this change applied in theAppDelegate.mofExampleproject).
On Android, no additional setup is required, although, you should keep in mind that once you set the orientation or status bar props, react-native-screens will manage them on every screen, so you shouldn't use other methods of manipulating them then.
screenOrientation
Sets the current screen's available orientations and forces rotation if current orientation is not included. On iOS, if you have supported orientations set in info.plist, they will take precedence over this prop. Possible values:
default- on iOS, it resolves to UIInterfaceOrientationMaskAllButUpsideDown. On Android, this lets the system decide the best orientation.allportraitportrait_upportrait_downlandscapelandscape_leftlandscape_right
Defaults to default.
statusBarAnimation
Sets the status bar animation (similar to the StatusBar component). Possible values: fade, none, slide. On Android, this prop considers the transition of changing status bar color (see https://reactnative.dev/docs/statusbar#animated). There will be no animation if none provided.
Defaults to fade on iOS and none on Android.
statusBarColor (Android only)
Sets the status bar color (similar to the StatusBar component). Defaults to initial status bar color.
statusBarHidden
Boolean saying if the status bar for this screen is hidden.
Defaults to false.
statusBarStyle
Sets the status bar color (similar to the StatusBar component). On iOS, the possible values are: auto (based on user interface style, inverted (colors opposite to auto), light, dark. On Android, the status bar will be dark if set to dark and light otherwise.
Defaults to auto.
statusBarTranslucent (Android only)
Sets the translucency of the status bar (similar to the StatusBar component). Defaults to false.
Search bar
The search bar is just a searchBar property that can be specified in the navigator's screenOptions or an individual screen's options. Search bars are rarely static so normally it is controlled by passing an object to searchBar navigation option in the component's body.
Example:
React.useLayoutEffect(() => {
navigation.setOptions({
searchBar: {
// search bar options
},
});
}, [navigation]);
We advise using useLayoutEffect hook instead of useEffect when managing searchBar props to avoid unexpected layout issues.
Supported properties are described below.
autoCapitalize
Controls whether the text is automatically auto-capitalized as it is entered by the user. Possible values:
nonewordssentencescharacters
Defaults to sentences on iOS and 'none' on Android.
autoFocus (Android only)
When set to true focuses search bar automatically when screen is appearing. Default value is false.
barTintColor
The search field background color.
By default bar tint color is translucent.
tintColor (iOS only)
The color for the cursor caret and cancel button text.
cancelButtonText (iOS only)
The text to be used instead of default Cancel button text.
disableBackButtonOverride (Android only)
Default behavior is to prevent screen from going back when search bar is open (disableBackButtonOverride: false). If you don't want this to happen set disableBackButtonOverride to true
hideNavigationBar (iOS only)
Boolean indicating whether to hide the navigation bar during searching.
Defaults to true.
hideWhenScrolling (iOS only)
Boolean indicating whether to hide the search bar when scrolling.
Defaults to true.
inputType (Android only)
This prop is used to change type of the input and keyboard. Default value is 'text'.
All values:
'text'- normal text input'number'- number input'email'- email input'phone'- phone input
obscureBackground (iOS only)
Boolean indicating whether to obscure the underlying content with semi-transparent overlay.
Defaults to true.
onBlur
A callback that gets called when search bar has lost focus.
onCancelButtonPress
A callback that gets called when the cancel button is pressed.
onChangeText
A callback that gets called when the text changes. It receives the current text value of the search bar.
Example:
const [search, setSearch] = React.useState('');
React.useLayoutEffect(() => {
navigation.setOptions({
searchBar: {
onChangeText: event => setSearch(event.nativeEvent.text),
},
});
}, [navigation]);
onClose (Android only)
A callback that gets called when search bar is closing
onFocus
A callback that gets called when search bar has received focus.
onOpen (Android only)
A callback that gets called when search bar is expanding
onSearchButtonPress
A callback that gets called when the search button is pressed. It receives the current text value of the search bar.
placeholder
Text displayed when search field is empty.
Defaults to an empty string.
placement (iOS only)
Position of the search bar
Supported values:
automatic- the search bar is placed according to current layoutinline- the search bar is placed on the trailing edge of navigation barstacked- the search bar is placed below the other content in navigation bar
Defaults to stacked
textColor
The search field text color.
hintTextColor
The search hint text color. (Android only)
headerIconColor
The search and close icon color shown in the header. (Android only)
shouldShowHintSearchIcon
Show the search hint icon when search bar is focused. (Android only)
ref
A React ref to imperatively modify search bar. Supported actions:
focus- focus on search barblur- remove focus from search barclearText- clear text in search barsetText- set search bar's content to given stringcancelSearch- cancel search in search bar.toggleCancelButton(iOS only) - toggle cancel button display near search bar.
Events
The navigator can emit events on certain actions. Supported events are:
appear - deprecated
Use transitionEnd event with data.closing: false instead.
Event which fires when the screen appears.
Example:
React.useEffect(() => {
const unsubscribe = navigation.addListener('appear', e => {
// Do something
});
return unsubscribe;
}, [navigation]);
dismiss
Event which fires when the current screen is dismissed by hardware back (on Android) or dismiss gesture (swipe back or down).
Example:
React.useEffect(() => {
const unsubscribe = navigation.addListener('dismiss', e => {
// Do something
});
return unsubscribe;
}, [navigation]);
transitionStart
Event which fires when a transition animation starts.
Event data:
closing- Whether the screen will be dismissed or will appear.
Example:
React.useEffect(() => {
const unsubscribe = navigation.addListener('transitionStart', e => {
if (e.data.closing) {
// Will be dismissed
} else {
// Will appear
}
});
return unsubscribe;
}, [navigation]);
transitionEnd
Event which fires when a transition animation ends.
Event data:
closing- Whether the screen was dismissed or did appear.
Example:
React.useEffect(() => {
const unsubscribe = navigation.addListener('transitionEnd', e => {
if (e.data.closing) {
// Was dismissed
} else {
// Did appear
}
});
return unsubscribe;
}, [navigation]);
Helpers
The stack navigator adds the following methods to the navigation prop:
push
Pushes a new screen to the top of the stack and navigate to it. The method accepts the following arguments:
name- string - Name of the route to push onto the stack.params- object - Screen params to merge into the destination route (found in the pushed screen throughroute.params).
navigation.push('Profile', { name: 'Michaś' });
pop
Pops the current screen from the stack and navigates back to the previous screen. It takes one optional argument (count), which allows you to specify how many screens to pop back by.
navigation.pop();
popToTop
Pops all of the screens in the stack except the first one and navigates to it.
navigation.popToTop();
Additional options
Measuring header's height
To measure header's height, you can use the useHeaderHeight, useAnimatedHeaderHeight or useReanimatedHeaderHeight hook.
useHeaderHeightreturns the static header's height. The value provided by this hook changes when screen appears, when there's a change in header options or screen orientation. Use this hook if you're sure your header height won't change dynamically, or when the screen is heavy.useAnimatedHeaderHeightdynamically calculates the header's height. The value provided by this hook changes with every view layout (such as shrinking a large header into small one with a ScrollView). It returns an Animated.Value. Please beware of using this hook in heavy components, as it may result in performance issues.useReanimatedHeaderHeightalso dynamically calculates the header's height but uses React Native Reanimated under the hood. It returns an Animated.SharedValue. Make sure to wrap your Stack.Navigator withReanimatedScreenProviderbefore using this hook.
We recommend using useReanimatedHeaderHeight rather than useAnimatedHeaderHeight. See Shared Values vs Animated.Value section in React Native Reanimated's documentation for full comparison.
// for using useHeaderHeight
import { useHeaderHeight } from 'react-native-screens/native-stack';
// for using useAnimatedHeaderHeight
import { useAnimatedHeaderHeight } from 'react-native-screens/native-stack';
// for using useReanimatedHeaderHeight
import { useReanimatedHeaderHeight } from 'react-native-screens/reanimated';
Example
import { createNativeStackNavigator } from 'react-native-screens/native-stack';
const Stack = createNativeStackNavigator();
function MyStack() {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
headerTintColor: 'white',
headerStyle: { backgroundColor: 'tomato' },
}}>
<Stack.Screen
name="Home"
component={Home}
options={{
title: 'Awesome app',
}}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{
title: 'My profile',
}}
/>
<Stack.Screen
name="Settings"
component={Settings}
options={{
gestureEnabled: false,
}}
/>
</Stack.Navigator>
);
}