Skip to content

Conversation

@lixiaoyan
Copy link
Contributor

@lixiaoyan lixiaoyan commented Dec 22, 2025

window.innerWidth/Height and visualViewport.width/height are not equivalent. The window.innerWidth/Height includes the scrollbar, but the visualViewport.width/height does not.

It's safe to use window.innerWidth/Height as fallback values on iOS, because their scrollbar does not take spaces. But for desktop platforms, it's not suitable.

However, it does not yet cause any problems in RAC, because:

  1. We are currently just using this hook in a Modal. We disabled document scroll first, so window.innerHeight is always equal to visualViewport.height. (scrollbar-gutter: stable affects only the vertical scrollbar)
  2. We don't use useViewportSize().width anywhere, therefore the difference between window.innerWidth and visualViewport.width isn't noticeable. 1

It currently only affects those who use the hook directly. You can check https://stackblitz.com/edit/5qposhdl?file=src%2FExample.tsx for the issue.

function Example() {
  const viewportSize = useViewportSize();

  return (
    <div>
      {JSON.stringify(viewportSize)}
      <input />
      <div style={{ height: '200vh' }} />
    </div>
  );
}
Screen.Recording.2026-01-07.at.8.30.41.PM.mov

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:


Screen.Recording.2026-01-07.at.9.06.31.PM.mov

Footnotes

  1. The useViewportSize().width is incorrect even when used inside a Modal with scroll disabled. See the screen record above that tested on Firefox. (Chrome has another bug that causes visualViewport.width to include the scrollbar)

@snowystinger
Copy link
Member

The purpose of this PR is to use virtual viewports as much as possible. This helps us to maintain consistency between browsers.

Did you notice a discrepancy between browsers with this behaviour and if so, what was it? or is something misbehaving?

@lixiaoyan
Copy link
Contributor Author

The issue is that visualViewport.width may not be identical to either window.innerWidth or document.documentElement.clientWidth.
See: #9318 (comment)

@snowystinger
Copy link
Member

Thanks, so there are currently a lot of PRs open from you around this topic (much appreciated) and I'm unsure which ones I'm supposed to be reviewing and/or in what order. Additionally, many of them are missing tests or examples or test instructions where I can verify the behaviour before and after the change. I'm also finding it hard to determine each PR's motivation for being open. This makes them very challenging to review, would you mind consolidating the ones which are bugs vs the ones which are feature requests and adding that information to them?
Screenshot 2026-01-06 at 2 57 27 pm

@lixiaoyan
Copy link
Contributor Author

lixiaoyan commented Jan 7, 2026

@snowystinger

The top three PRs (#9393 #9392 #9384) are mostly independent bug fixes that do not rely on any other PRs. Although they were discovered during the last one PR #9318.

The last two PRs (#9318 #9383) are attempting to address the same problem - correctly placing a dialog on a page that has been horizontally scrolled - in two different ways. They do not depend on any other PRs either.

@lixiaoyan
Copy link
Contributor Author

lixiaoyan commented Jan 7, 2026

Edited: I've updated the PR title/description and updated the commit message to fix: ....


Original post

#9393 and #9384 are around the same topic. In the Web world, there are too many metrics about for viewport/page/document/window/... We should choose the fallback carefully, to avoid using any pair that are inconsistent.

Back to this PR. It's safe to use window.innerWidth/Height as fallback values on iOS, because their scrollbar does not take spaces. But for desktop platforms, it's not suitable.

However, it does not yet cause any problems, because:

  1. We are currently just using this hook in a Modal. We disabled document scrolling first, so window.innerHeight is always equal to visualViewport.height.
  2. We don't use useViewportSize().width anywhere, therefore the difference between window.innerWidth and visualViewport.width isn't noticeable.

So, yea, this is another correctness fix. I cannot provide a reproducible example with the RAC. It's currently only about this hook. You can check https://stackblitz.com/edit/5qposhdl?file=src%2FExample.tsx for the hook issue.

function Example() {
  const viewportSize = useViewportSize();

  return (
    <div>
      {JSON.stringify(viewportSize)}
      <input />
      <div style={{ height: '200vh' }} />
    </div>
  );
}
Screen.Recording.2026-01-07.at.8.30.41.PM.mov

@lixiaoyan lixiaoyan changed the title feat: only handle blur event for resizing viewport on iOS fix: only handle blur event for resizing viewport on iOS Jan 7, 2026
@lixiaoyan lixiaoyan force-pushed the use-viewport-size-only-handle-blur-on-ios branch from f73c297 to 272f653 Compare January 7, 2026 13:18
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.

2 participants