Skip to content

fix(storybook-rn-web): absolute-positioned components don't render correctly in web Storybook #1196

@georgewrmarshall

Description

@georgewrmarshall

Summary

Components that rely on position: absolute with safe-area-based dimensions (BottomSheet, BottomSheetDialog, BottomSheetOverlay) render incorrectly or are invisible in the web Storybook (@storybook/react-native-web-vite). The same components work correctly in the iOS Storybook.

Affected components

  • BottomSheet — dialog not visible after opening
  • BottomSheetDialog — never visible (auto-opens on mount but stays off-screen)
  • BottomSheetOverlay — backdrop not visible

Root cause

Two related problems:

1. CSS positioning context

On iOS, React Native's position: absolute is always relative to the direct parent component. On web (via react-native-web), it follows CSS rules and needs a positioned ancestor (position: relative/absolute/fixed).

The GestureHandlerRootView + SafeAreaProvider chain in preview.tsx does not establish a reliable positioned CSS context with a defined height, so absolute inset-0 / absolute bottom-0 inset-x-0 resolve to the wrong ancestor.

2. useSafeAreaFrame() returns window dimensions on web

BottomSheetDialog uses:

const { height: screenHeight } = useSafeAreaFrame();
const maxSheetHeight = screenHeight - screenTopPadding;

On web, useSafeAreaFrame() returns the browser window dimensions rather than the story canvas dimensions. This causes:

  • maxHeight: maxSheetHeight to be calibrated to the full window height, not the preview iframe
  • The animation translateY start/end values to be off
  • maxHeight: 0 if SafeAreaProvider hasn't initialized yet on first render

Expected behaviour

BottomSheetDialog slides up from the bottom of the preview canvas and is fully visible, matching the iOS behaviour (without gesture/animation fidelity — that's expected on web).

Proposed fix

Two options to evaluate:

Option A — Story-level fix (lower risk):
Add a position: relative wrapper with a defined height to the affected stories' decorators on web, e.g.:

decorators: [
  (Story) => (
    <View style={{ flex: 1, position: 'relative' }}>
      <Story />
    </View>
  ),
],

Option B — Component-level fix (correct long-term):
Branch on Platform.OS === 'web' in BottomSheetDialog to skip useSafeAreaFrame and use layout-relative sizing instead:

const screenHeight = Platform.OS === 'web'
  ? containerHeight  // measured via onLayout on the parent
  : useSafeAreaFrame().height;

Option A is safer as a patch; Option B is more correct and would also benefit production web usage.

Context

Discovered while enabling web Storybook in PR #1134. The iOS Storybook remains the source of truth for these components in the meantime.

Related PR: #1134
Upstream tracking: dannyhw/vite-plugin-rnw#13

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions