You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dynamic item sizes are not recalculated after reordering #1084
In a virtualized list using @tanstack/vue-virtual, reordering items with dynamic or varying sizes (e.g., different heights) does not trigger a recalculation of their cached measurements. As a result, the items retain their initial rendered dimensions post-reorder, leading to visual artifacts such as:
Larger items being clipped or overflowing within the constrained space originally allocated to smaller items.
Smaller items leaving empty gaps due to mismatched container sizes.
After reordering, the virtualizer should re-measure and update the sizes for affected items
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Windows - Chrome - v.142.0.7444.61
tanstack-virtual version
v3.13.12
TypeScript version
v5.4.5
Additional context
No response
Terms & Code of Conduct
I agree to follow this project's Code of Conduct
I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
Reacted by Ilya Golovin, Filimon Mikhail, Pasha, Alexander Ryzhikh, Paweł and Denis Grushkin
I found a reference where @piecyk recommends using useCallback to solve a (looks like) similar issue. In vue, however, we don't have such an API and rely on a smarter reactivity system rather than handling dependencies manually.
The measurement cache is keyed by getItemKey, which defaults to the index, so after a reorder positions keep their old sizes (and nothing resizes, so ResizeObserver doesn't fire). Use a stable key, e.g. getItemKey: (i) => items.value[i].id; a new getItemKey invalidates the measurements. Calling virtualizer.measure() after a reorder also works. Please reopen with an updated repro if it still breaks with a stable key.
Thanks for the repro. It doesn't use measureElement, so every size comes from estimateSize. getItemKey only keys measured sizes, so it has no effect here, and the virtualizer can't tell that estimateSize(i) now returns something different. Two options:
Measure the rows: :ref="rowVirtualizer.measureElement" + :data-index="virtualRow.index" on the row (and drop the fixed height). With getItemKey by id, the sizes follow the items after a swap.
Keep estimates and call rowVirtualizer.value.measure() after the swap.
Also, :key="virtualRow.id" should be virtualRow.key, and pass the options as a computed so count stays reactive.
Activity
ilyarefraction commentedon Dec 1, 2025
This issue can be "fixed" in the userland by calling virtualizer.measure() when the ordering in the item list is changed. The issues are:
IMO, ideally, the library should automatically handle size recalculation during reordering
ilyarefraction commentedon Dec 1, 2025
I found a reference where @piecyk recommends using
useCallbackto solve a (looks like) similar issue. In vue, however, we don't have such an API and rely on a smarter reactivity system rather than handling dependencies manually.piecyk commentedon Oct 14, 2026
The measurement cache is keyed by
getItemKey, which defaults to the index, so after a reorder positions keep their old sizes (and nothing resizes, so ResizeObserver doesn't fire). Use a stable key, e.g.getItemKey: (i) => items.value[i].id; a newgetItemKeyinvalidates the measurements. Callingvirtualizer.measure()after a reorder also works. Please reopen with an updated repro if it still breaks with a stable key.Misha6787 commentedon Oct 22, 2026
@piecyk Thanks for the response. I added the use of
getItemKey: (i) => items.value[i].id, but that didn't help.The option using
getItemKey: (i) => items.value[i].id- https://stackblitz.com/edit/tanstack-virtual-jcjdebwxUPD:
For some reason I can't reopen the issue(
piecyk commentedon Oct 22, 2026
Thanks for the repro. It doesn't use
measureElement, so every size comes fromestimateSize.getItemKeyonly keys measured sizes, so it has no effect here, and the virtualizer can't tell thatestimateSize(i)now returns something different. Two options::ref="rowVirtualizer.measureElement"+:data-index="virtualRow.index"on the row (and drop the fixedheight). WithgetItemKeyby id, the sizes follow the items after a swap.rowVirtualizer.value.measure()after the swap.Also,
:key="virtualRow.id"should bevirtualRow.key, and pass the options as acomputedsocountstays reactive.Misha6787 commentedon Oct 24, 2026
@piecyk It works! Thank you very much for your help.
Just in case, I’ll leave a link to the version with your edits in this comment. It might come in handy for someone.
https://stackblitz.com/edit/tanstack-virtual-jcjdebwx
UPD: I forgot to mention that I used option 1 to fix the problem.