Description
When an in-layout <BottomSheet> (not modal / nativeOverlay) lives on a screen inside @react-navigation/native-stack, popping that screen on Android makes the sheet expand from its current detent to ~full height (everything below the header) at the very start of the exit transition, then slide out at that full height. The sheet ignores its own detents during this moment.
iOS is not affected — the sheet keeps its detent height throughout the exit.
This reproduces with a bare <BottomSheet>: fixed pixel detents, no wrapNativeView, no reanimated, animateIn default. So it is not related to any animated wrapper or controlled-index churn on the app side.
Minimal reproduction
Full runnable repo: https://github.com/LeCiel/swmansion-bottom-sheet-native-stack-android-repro
The entire repro is one App.tsx — a 2-screen native-stack; screen B renders a bare in-layout sheet:
import React from "react";
import { Button, StyleSheet, Text, View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { BottomSheet } from "@swmansion/react-native-bottom-sheet";
const Stack = createNativeStackNavigator();
function HomeScreen({ navigation }: any) {
return (
<View style={[styles.fill, styles.center]}>
<Text style={styles.title}>Screen A</Text>
<Button title="Go to Screen B (sheet)" onPress={() => navigation.navigate("B")} />
</View>
);
}
function SheetScreen() {
return (
<View style={styles.fill}>
<BottomSheet index={1} detents={[220, 480]}>
<View style={styles.sheetContent}>
<Text style={styles.sheetLabel}>Sheet content (fixed detent = 480px)</Text>
</View>
</BottomSheet>
</View>
);
}
export default function App() {
return (
<GestureHandlerRootView style={styles.fill}>
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="A" component={HomeScreen} />
<Stack.Screen name="B" component={SheetScreen} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
fill: { flex: 1 },
center: { alignItems: "center", justifyContent: "center", gap: 16, padding: 24 },
title: { fontSize: 22, fontWeight: "600" },
sheetContent: {
flex: 1,
backgroundColor: "#7C4DFF",
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
alignItems: "center",
paddingTop: 24,
},
sheetLabel: { color: "white", fontSize: 16, fontWeight: "600" },
});
Steps
- Launch on Android (
npx expo run:android — native module, so a build is required, not Expo Go).
- Tap Go to Screen B — the sheet sits at the 480px detent.
- Press back (button or system back gesture).
- Watch the sheet: it snaps to ~full height as the exit begins, then slides out at that height.
Expected
The sheet keeps its detent height (480px) throughout the exit transition, as it does on iOS.
Actual (Android only)
At the first frame of the pop, the sheet jumps from 480px to ~full height (header to bottom), overshooting its largest detent, then slides out expanded.
| Before back (stable at 480px detent) |
During exit (expands to ~full height) |
 |
 |
Screen recording: repro-media/exit-transition.mp4 (frame-by-frame at 30fps, the sheet is already full height while the header still reads "B", before the screen starts sliding out).
Hypothesis
During the Android Fragment exit transition, react-native-screens appears to re-host / re-measure the outgoing screen at full window height; the in-layout sheet's height constraint seems to be recomputed to the full host rather than the resolved detent, so it expands. (iOS's UINavigationController transition doesn't re-measure the outgoing view host the same way.)
Environment
@swmansion/react-native-bottom-sheet 0.15.3
react-native-screens 4.25.2
@react-navigation/native-stack 7.18.4, @react-navigation/native 7.3.x
- react-native 0.85.3, React 19.2.3, Expo SDK 56
- New Architecture (Fabric, bridgeless)
- No reanimated installed; bare
<BottomSheet>
- Android emulator (Pixel 8, API 35). iOS unaffected.
Description
When an in-layout
<BottomSheet>(notmodal/nativeOverlay) lives on a screen inside@react-navigation/native-stack, popping that screen on Android makes the sheet expand from its current detent to ~full height (everything below the header) at the very start of the exit transition, then slide out at that full height. The sheet ignores its owndetentsduring this moment.iOS is not affected — the sheet keeps its detent height throughout the exit.
This reproduces with a bare
<BottomSheet>: fixed pixel detents, nowrapNativeView, no reanimated,animateIndefault. So it is not related to any animated wrapper or controlled-index churn on the app side.Minimal reproduction
Full runnable repo: https://github.com/LeCiel/swmansion-bottom-sheet-native-stack-android-repro
The entire repro is one
App.tsx— a 2-screen native-stack; screen B renders a bare in-layout sheet:Steps
npx expo run:android— native module, so a build is required, not Expo Go).Expected
The sheet keeps its detent height (480px) throughout the exit transition, as it does on iOS.
Actual (Android only)
At the first frame of the pop, the sheet jumps from 480px to ~full height (header to bottom), overshooting its largest detent, then slides out expanded.
Screen recording:
repro-media/exit-transition.mp4(frame-by-frame at 30fps, the sheet is already full height while the header still reads "B", before the screen starts sliding out).Hypothesis
During the Android Fragment exit transition,
react-native-screensappears to re-host / re-measure the outgoing screen at full window height; the in-layout sheet's height constraint seems to be recomputed to the full host rather than the resolved detent, so it expands. (iOS'sUINavigationControllertransition doesn't re-measure the outgoing view host the same way.)Environment
@swmansion/react-native-bottom-sheet0.15.3react-native-screens4.25.2@react-navigation/native-stack7.18.4,@react-navigation/native7.3.x<BottomSheet>