Skip to content

Keyed x-for with seems to not be robust against adding/removing elements #4843

Description

@mikejsavage

Alpine.js version

v3.15.12

Browser and operating system

Safafi/Vivaldi (chrome) on macOS, Chrome on Windows

Describe the issue you're experiencing

I am trying to implement virtual scrolling on a large photo album. My approach is to make a div sized to the height of the full album, and put another div inside that with its top set to slightly above the window scroll region and render a subrange of the images inside that.

Or in slightly simplified code:

<div :style="{ height: height }">
        <div :style="{ top: top }" @scroll.window="UpdateLayout()">
            <template x-for="i in visible_end - visible_start">
                <img src="photos[visible_start + i - 1]">
            </template>
        </div>
</div>

This works fine, except that Alpine reuses img tags, and if you change an img's src it will show the old image (thumbnail in this case) until the new one has fully loaded, which is visibly janky while scrolling the page.

I believe the standard fix for this is to add :key=visible_start + i to force Alpine to create new elements, which it does, but it also seems like Alpine gets confused by the additions/removals and eventually messes up the page.

Code snippets to reproduce the issue

See this PoC: https://mikejsavage.co.uk/alpinejs.html

Scroll around for a bit and you will notice that 1) all the elements can "jump" up or down when they shouldn't and 2) sometimes elements get duplicated, most obviously sometimes if you scroll all the way back to the top the first row gets duplicated. If you remove :key in the inspector it all works as intended.

Screenshots/screen recordings

Screen.Recording.2026-06-14.at.10.00.24.am.mov

How do you expect it to work?

.

Please confirm (incomplete submissions will not be addressed)

  • I have provided easy and step-by-step instructions to reproduce the bug.
  • I have provided code samples as text and NOT images.
  • I understand my bug report will be removed if I haven't met the criteria above.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

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