Shopify / Shopify/flash-list

[WEB] Scrollbar oscillation cause infinite layout loop

Open
#2,334 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
7.2k
Forks
393
Avg merge
1d 3h
Merged PRs (30d)
1

Description

Description

On web with classic (non-overlay) scrollbars — i.e. Windows/Linux Chrome and macOS with "Always show scroll bars", FlashList v2 can enter an infinite layout loop and crash with Maximum update depth exceeded because the vertical scrollbar's own appearance/disappearance feeds back into the list's cross-axis (width) measurement.

When list content height sits right at the threshold where it just barely overflows the viewport, the browser shows a vertical scrollbar. The scrollbar consumes ~15–17px of width, which reduces the measured client width FlashList uses as boundedSize. The narrower width re-flows items so the content no longer overflows → the scrollbar is removed → the measured width grows back → content overflows again → the scrollbar returns → and so on, forever.

This is web-specific: native platforms use overlay scrollbars that don't consume layout width, so they never oscillate.

Current behavior

  • The list rapidly re-lays-out, the scrollbar visibly flickers, and React throws Maximum update depth exceeded (setState loop in updateLayoutParams → recompute layout → measure → updateLayoutParams …), crashing the app.
  • Happens whenever content size is near the boundary at which adding/removing the scrollbar flips whether the content overflows.

Expected behavior

FlashList should not let the scrollbar's own width feedback-loop the layout.

Reproduction

Expo Snack or minimal reproduction link:

Make sure to test on web with classic (non-overlay) scrollbar. If you use MacOS fo to Settings -> Appearance -> Windows -> Show scroll bars -> Always.

https://snack.expo.dev/@vikstash/flashlist_loop_crash

Platform

  • iOS
  • Android
  • Web (if applicable)

Environment

React Native info output:
Paste output here

FlashList version:
2.3.0

Additional context

Root cause is in LinearLayoutManager.ts, method updateLayoutParams. boundedSize is taken directly from the measured window size with no detection of scrollbar-induced oscillation, so the scrollbar appearing/disappearing keeps changing the measured width and re-triggering layout.

Checklist

  • I've searched existing issues and couldn't find a duplicate
  • I've provided a minimal reproduction (Expo Snack preferred)
  • I'm using the latest version of @shopify/flash-list
  • I've included all required information above

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/recyclerview/layout-managers/LinearLayoutManager.ts, especially updateLayoutParams and its boundedSize calculation. Reproduce the crash using the linked Expo Snack on web with classic scrollbars, then trace the updateLayoutParams → layout → measure cycle. Done means the scrollbar no longer causes an infinite layout loop or Maximum update depth exceeded error.

Written by the indexing model from the issue text.

Assessment

Tech stack
react-native, typescript
Domain
frontend, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.