Skip to content

Commit d284d4f

Browse files
author
전예진(양파유닛)
committed
feat(virtual-core): add skipRemeasurementOnBackwardScroll option to reduce stuttering, especially when scrolling lists with dynamically sized items
1 parent e06e32d commit d284d4f

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

.changeset/witty-bears-fail.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@tanstack/virtual-core': minor
3+
---
4+
5+
feat: add skipRemeasurementOnBackwardScroll option to reduce stuttering
6+
7+
Adds new option to skip re-measuring already-cached items during backward scrolling.
8+
This prevents scroll adjustments that conflict with the user's scroll direction, reducing stuttering in dynamic content like social feeds or chat messages.
9+
10+
When enabled, cached measurements are reused during backward scroll while `isScrolling` is true. Layout settles correctly once scrolling stops.
11+
12+
**Changes:**
13+
- Added `skipRemeasurementOnBackwardScroll` option (default: `false`)
14+
- Skip re-measurement in `_measureElement` when scrolling backward with cached items

docs/api/virtualizer.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ This option enables wrapping ResizeObserver measurements in requestAnimationFram
270270

271271
It helps prevent the "ResizeObserver loop completed with undelivered notifications" error by ensuring that measurements align with the rendering cycle. This can improve performance and reduce UI jitter, especially when resizing elements dynamically. However, since ResizeObserver already runs asynchronously, adding requestAnimationFrame may introduce a slight delay in measurements, which could be noticeable in some cases. If resizing operations are lightweight and do not cause reflows, enabling this option may not provide significant benefits.
272272

273+
### `skipRemeasurementOnBackwardScroll`
274+
275+
```tsx
276+
skipRemeasurementOnBackwardScroll: boolean
277+
```
278+
When enabled, prevents re-measuring items that have already been measured during backward scrolling.
279+
This reduces stuttering caused by scroll position adjustments that conflict with the user's scroll direction.
280+
It is recommended to use this property when scrolling in situations where item heights change dynamically.
281+
273282
## Virtualizer Instance
274283

275284
The following properties and methods are available on the virtualizer instance:

packages/virtual-core/src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ export interface VirtualizerOptions<
348348
enabled?: boolean
349349
isRtl?: boolean
350350
useAnimationFrameWithResizeObserver?: boolean
351+
skipRemeasurementOnBackwardScroll?: boolean
351352
}
352353

353354
export class Virtualizer<
@@ -447,6 +448,7 @@ export class Virtualizer<
447448
isRtl: false,
448449
useScrollendEvent: false,
449450
useAnimationFrameWithResizeObserver: false,
451+
skipRemeasurementOnBackwardScroll: false,
450452
...opts,
451453
}
452454
}
@@ -879,6 +881,21 @@ export class Virtualizer<
879881
}
880882

881883
if (node.isConnected) {
884+
// Check if we should skip remeasuring during backward scroll
885+
if (
886+
this.options.skipRemeasurementOnBackwardScroll &&
887+
this.scrollDirection === 'backward' &&
888+
this.isScrolling
889+
) {
890+
const isAlreadyMeasured = this.itemSizeCache.has(key)
891+
if (isAlreadyMeasured) {
892+
// Skip remeasuring to prevent stuttering during backward scroll
893+
// Use cached measurement instead
894+
return
895+
}
896+
}
897+
898+
// Measure and update size
882899
this.resizeItem(index, this.options.measureElement(node, entry, this))
883900
}
884901
}

0 commit comments

Comments
 (0)