Skip to content

fix: make nested SafeAreaProvider report its own frame and insets on web#737

Draft
janicduplessis wants to merge 2 commits into
mainfrom
@janic/web-nested-provider
Draft

fix: make nested SafeAreaProvider report its own frame and insets on web#737
janicduplessis wants to merge 2 commits into
mainfrom
@janic/web-nested-provider

Conversation

@janicduplessis

@janicduplessis janicduplessis commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #713

On web, NativeSafeAreaProvider always reported window-level values: insets from the hidden env(safe-area-inset-*) measurement element and the frame as document.documentElement dimensions, no matter where the provider is rendered. Nested SafeAreaProviders therefore returned root window metrics from useSafeAreaFrame / useSafeAreaInsets, unlike iOS/Android where each provider reports its own view's frame and insets.

The provider now measures its wrapper element with getBoundingClientRect() (on react-native-web the View ref is the underlying DOM element):

  • the frame is the element's rect in viewport coordinates, matching the native frame in window coordinates
  • insets are the window env() insets clamped to the part of the safe area overlapping the element: max(0, windowInset - distanceFromThatEdge)
  • a ResizeObserver re-measures when the element resizes, and a capture-phase window scroll listener re-measures when an ancestor scroll moves it
  • Fallback: when the element can't be measured (SSR, no ResizeObserver support) it keeps the previous window-level behavior. The hidden-element env() transition mechanism is unchanged as the window-level inset source.

Known limitation: a layout shift that moves the provider without resizing it and without scrolling (e.g. a sibling collapsing above it) won't re-report until the next resize/scroll/env() change.

Adds jest-environment-jsdom as a devDependency for the new tests.

Test Plan

New jsdom tests in src/__tests__/NativeSafeAreaProviderWeb-test.tsx render the web provider with react-dom and stub getBoundingClientRect, getComputedStyle and ResizeObserver:

  • a provider positioned away from the window edges reports its own rect as the frame and clamped (zero or partial) insets — these failed before this change with window-level values
  • a full-viewport provider reports the same insets/frame as before (regression guard)
  • metrics update when the observed element resizes, and when an ancestor scroll moves it without resizing it (also failed before, values stayed stale)
  • without ResizeObserver it falls back to window metrics

Ran with yarn validate:jest (all suites pass), plus prettier/eslint/tsc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Web] Nested <SafeAreaProvider> doesn’t update frame values

1 participant