Skip to content

Fix item jumps in masonry layout#2108

Open
j0sip wants to merge 4 commits intoShopify:mainfrom
j0sip:issue-2107-fix-masonry-column-jumpts
Open

Fix item jumps in masonry layout#2108
j0sip wants to merge 4 commits intoShopify:mainfrom
j0sip:issue-2107-fix-masonry-column-jumpts

Conversation

@j0sip
Copy link
Contributor

@j0sip j0sip commented Feb 20, 2026

Description

Fixes #2107

This PR prevents items from "jumping" between columns in the Masonry Layout when optimizedItemArrangement is enabled. It ensures that resizing an item doesn't disrupt the user's visual flow by locking the column positions.

Currently, when an item’s size changes, the Masonry algorithm recomputes the entire layout for all subsequent items. While this optimization helps fill gaps, it causes visible items to jump to different columns, causing a bad UX for end users.

To improve the UX, I've introduced a lastLockedItemIndex which is the index of last engaged item. It ensures that all items up to that index retain their previously calculated column. Recomputation of columns would now only be allowed for items above that index (i.e. non-visible items), so the end user wouldn't notice it.

Reviewers’ hat-rack 🎩

  • [ ]

Screenshots or videos (if needed)

Tested in ComplexMasory in fixture app:
after

@naqvitalha
Copy link
Collaborator

naqvitalha commented Feb 26, 2026

Thanks for the PR and the detailed write-up!

I think there's a simpler path here. The jumping happens because optimizeItemArrangement recomputes column assignments to balance column heights — that's its job. When an item resizes, it re-runs that optimization and items move to different columns.

Column locking fights against that optimization. And with multi-span items, locking can create visible gaps (e.g., a span=2 item must start at the tallest column's height, leaving empty space in shorter columns).

The simpler solution: if your items resize dynamically, use optimizeItemArrangement: false. Sequential placement means items never change columns — there's nothing to "jump." The layout won't be perfectly balanced, but items stay stable, which is what you want for dynamic content.

Think of it as:

  • optimizeItemArrangement: true — best for items with fixed/stable heights. Produces a balanced layout.
  • optimizeItemArrangement: false — best for items that resize after mounting. Items stay in their column but devs manager the sequence.

@j0sip
Copy link
Contributor Author

j0sip commented Feb 26, 2026

I think there's a simpler path here. The jumping happens because optimizeItemArrangement recomputes column assignments to balance column heights — that's its job. When an item resizes, it re-runs that optimization and items move to different columns.

The simpler solution: if your items resize dynamically, use optimizeItemArrangement: false. Sequential placement means items never change columns — there's nothing to "jump." The layout won't be perfectly balanced, but items stay stable, which is what you want for dynamic content.

Thanks for the reply @naqvitalha . We have tried using optimizeItemArrangement: false, but it easily turns into unbalanced column heights for larger lists, which does not look good from UI/UX perspective. Ideally, we would want both: balanced columns and allow dynamic resizing, but I understand that's not possible right now.

Perhaps another solution that could be considered is if we extend optimizeItemArrangement prop to be an enum instead, something like:

  • optimizeItemArrangement: always - same as current true
  • optimizeItemArrangement: never - same as current false
  • optimizeItemArrangement: onFirstLayout - this option can allow locked columns after first layout, preventing the jumping cards

Boolean values could still be supported for backward compatibility. Could you consider that approach?

And with multi-span items, locking can create visible gaps (e.g., a span=2 item must start at the tallest column's height, leaving empty space in shorter columns).

Gaps with multi-span appear even in current implementation when items resize, because most of the time none of the items can fit the gap. It's evident in fixture app (ComplexMasonry) as well. Additionally, I believe small gap is better than items jumping between columns.

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.

Masonry items jump between columns upon size change

2 participants