In React Navigation 7's native stack, how does the presentation option change how an audiobook player screen appears, and what differs on Android?
answer
- card is the default push
- modal slides up over the stack
- transparentModal keeps the previous screen
- iOS modal styles fall back on Android
- formSheet with detents
basics
~20 spresentation defaults to 'card', a normal push. 'modal' presents the screen modally, 'transparentModal' keeps the previous screen visible behind it, and iOS styles such as 'fullScreenModal' or 'pageSheet' fall back to a plain modal on Android.
solid answer
~40 sIn the native stack, `presentation` decides how a pushed screen is shown. `'card'` (the default) is a normal push with the platform's side transition. `'modal'` presents it modally (typically a card-style sheet on iOS) and can host a nested stack. `'transparentModal'` also presents modally but leaves the previous screen rendered, so a translucent background shows the content beneath. The iOS-specific styles map to UIKit modal presentation styles: `'fullScreenModal'`, `'pageSheet'`, `'containedModal'` and `'containedTransparentModal'`, and the option's documentation says each falls back to `'modal'` or `'transparentModal'` on Android. `'formSheet'` gives a resizable native sheet controlled by `sheetAllowedDetents`. For an audiobook app, the Player is a natural `'modal'` over the tabs, a sleep-timer picker a `'formSheet'` with detents. Set it per screen in `options` or for a set of screens in a group's `screenOptions`.
code
tsx · 34 linesimport { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Text } from 'react-native';
function LibraryScreen() {
return <Text>Library</Text>;
}
function PlayerScreen() {
return <Text>Now playing</Text>;
}
function SleepTimerScreen() {
return <Text>Stop after this chapter</Text>;
}
export const RootStack = createNativeStackNavigator({
screens: {
Library: LibraryScreen,
},
groups: {
Overlays: {
screenOptions: { headerShown: false },
screens: {
Player: { screen: PlayerScreen, options: { presentation: 'modal' } },
SleepTimer: {
screen: SleepTimerScreen,
options: {
presentation: 'formSheet',
sheetAllowedDetents: [0.4, 1],
sheetGrabberVisible: true,
},
},
},
},
},
});go deeper
Recall that presentation defaults to 'card' and that 'modal' presents a screen over the stack, set in a screen's options.
Explain modal versus transparentModal, the formSheet detent options, and which iOS styles fall back on Android.
Choose presentations that match each platform's conventions, group modal screens, and handle headers and dismissal for modal flows.
Agree on when the product uses modals, sheets or pushes so navigation feels consistent and platform-appropriate across teams.
## What `presentation` controls In a stack, every screen is on top of the previous one, but not every screen should look like a page you drilled into. A full-screen audiobook player, a sleep-timer picker or a confirmation overlay are better shown as something **presented over** the current context. The native stack's `presentation` option chooses how. | Value | What the user sees | Android | |---|---|---| | `'card'` (default) | A normal push with the platform transition | Platform push | | `'modal'` | A modal presentation; can contain its own stack | Modal | | `'transparentModal'` | Modal, previous screen stays rendered underneath | Transparent modal | | `'fullScreenModal'` | iOS full-screen modal style | Falls back to `'modal'` | | `'pageSheet'` | iOS page sheet style | Falls back to `'modal'` | | `'containedModal'` | iOS current-context modal style | Falls back to `'modal'` | | `'containedTransparentModal'` | iOS over-current-context style | Falls back to `'transparentModal'` | | `'formSheet'` | A native sheet with detents | Documented inconsistently; test on Android | The option is supported only on iOS and Android. ## `modal` and `transparentModal` - **`'modal'`** is the choice for a self-contained task: the audiobook Player, a "new playlist" form. On iOS it typically appears as a card-style sheet over the current screen. The option's documentation notes it can host a nested stack, so a multi-step flow can live inside the modal. - **`'transparentModal'`** keeps the previous screen rendered, so if the new screen's background is translucent, the content beneath shows through. It fits overlays such as a dimmed "chapter finished" prompt. The screen must set its own translucent background; the option only stops the screen underneath from being removed. ## iOS modal styles `'fullScreenModal'`, `'pageSheet'`, `'containedModal'` and `'containedTransparentModal'` map to UIKit's modal presentation styles (`UIModalPresentationFullScreen` and so on). They exist so a React Native screen can match an iOS convention precisely. The option's documentation states that on Android each falls back to `'modal'` or, for the transparent variant, `'transparentModal'`, so do not design Android behaviour that depends on them. ## `formSheet` and detents `'formSheet'` presents a native sheet whose height is controlled by options that only apply to it: - **`sheetAllowedDetents`**: ascending fractions of the screen height, such as `[0.4, 1]`, or `'fitToContents'`; defaults to `[1.0]`. The documentation notes that Android uses at most three values. - **`sheetGrabberVisible`**, **`sheetCornerRadius`**, **`sheetInitialDetentIndex`** and **`sheetLargestUndimmedDetentIndex`** fine-tune it. For the audiobook app, a sleep-timer or playback-speed picker as a `formSheet` at 40 % height keeps the player visible above it. On Android, check the result on a device: the `presentation` option's comment says `'formSheet'` falls back to `'modal'` there, while the detent options document Android-specific limits, so the two comments do not agree. ## Where to set it - **Per screen**: `options: { presentation: 'modal' }` on the Player screen. - **For a set of screens**: a group whose `screenOptions` sets `presentation: 'modal'`, so every screen added to the group is presented the same way. A modal screen is still part of the same stack: it has a route, receives params, and `goBack` dismisses it. ## Choosing for the audiobook app 1. **Book details and chapter lists**: `'card'`, because they are drill-down history. 2. **The full player**: `'modal'`, a self-contained task dismissed back to where the user was. 3. **Sleep timer and playback speed**: `'formSheet'` with detents, so the player stays visible. 4. **A "chapter finished" overlay**: `'transparentModal'` with a translucent background. ## Related options and pitfalls - **Animation**: `presentation` sets the style of appearance; the `animation` option picks among named transitions and some values depend on it, for example `'flip'` requires `presentation: 'modal'` and is iOS-only. - **Header**: a modal usually needs its own close button, since the back arrow of a pushed card is not the convention for a modal. - **The JS stack is different**: `createStackNavigator` supports only `'card'`, `'modal'` and `'transparentModal'`, and its modals are animated in JavaScript.
- You set presentation: 'transparentModal' but the previous screen still is not visible behind the overlay. What is missing?`'transparentModal'` only keeps the previous screen rendered underneath. The new screen still has an opaque background unless you give it a translucent one, for example through `contentStyle` or the screen's root view style.
- Why should Android behaviour not rely on presentation: 'pageSheet'?It is an iOS modal presentation style. The option's documentation says it falls back to `'modal'` on Android, so the page-sheet look and its dismissal behaviour exist only on iOS. Design the Android experience around a plain modal.
saying these in an interview costs you the question
- presentation: 'modal' needs a separate navigator because modals are not part of the stack.
- transparentModal makes the new screen's background transparent automatically.
- fullScreenModal and pageSheet look the same on Android as on iOS.
- The JS stack supports formSheet with sheetAllowedDetents too.
- presentation defaults to 'modal' for screens without a header.