Skip to content

[Bug]: BottomSheetModal portal entry orphans when parent screen unmounts without dismiss() #2644

@ddelange

Description

@ddelange

Version

v5

Reanimated Version

v3

Gesture Handler Version

v2

Platforms

iOS

What happened?

When a React Navigation stack screen containing a presented BottomSheetModal is popped without calling dismiss() first, the modal's portal entry is never removed from the @gorhom/portal reducer state. The PortalHost continues rendering the stale BottomSheet tree — including its BackdropComponent with
StyleSheet.absoluteFillObject and pointerEvents="auto" — blocking all touches app-wide.

Root cause in source: handlePortalOnUnmount in BottomSheetModal.tsx (~line 311) ignores the removePortal callback passed to it by Portal's unmount effect. Instead it tries bottomSheetRef.current?.close() and relies on the animation completing to eventually call unmount()unmountPortal(). If bottomSheetRef.current is
already null (React tore down the inner BottomSheet before the Portal cleanup fires), close() never runs and the portal entry is never removed.

Suggested fix: handlePortalOnUnmount should accept and use the removePortal callback as a fallback when bottomSheetRef.current is null.

Workaround:
useEffect(() => {
const ref = bottomSheetRef.current;
return () => { ref?.dismiss(); };
}, []);

Reproduction steps

  1. Wrap your app in BottomSheetModalProvider
  2. On a stack screen, render a BottomSheetModal and call present()
  3. Pop the screen (navigate back) without calling dismiss() first
  4. All touches on any screen are now blocked by the invisible orphaned backdrop

Reproduction sample

https://snack.expo.dev/@dcdevteam/bottom-sheet---issue-reproduction-template

Relevant log output

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions