0
# Virtualizer Core Engine
1
2
The core virtualization engine class that manages all aspects of virtual scrolling including item measurement, range calculation, scroll positioning, and DOM element lifecycle management.
3
4
## Capabilities
5
6
### Virtualizer Class
7
8
The main virtualization engine that handles all scrolling, measurement, and rendering calculations.
9
10
```typescript { .api }
11
/**
12
* Core virtualization engine for managing virtual scrolling
13
* @template TScrollElement - Type of the scroll container (Element or Window)
14
* @template TItemElement - Type of the virtualized item elements
15
*/
16
export class Virtualizer<TScrollElement extends Element | Window, TItemElement extends Element> {
17
constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>);
18
19
// Configuration
20
setOptions(opts: VirtualizerOptions<TScrollElement, TItemElement>): void;
21
22
// Virtual item management
23
getVirtualItems(): Array<VirtualItem>;
24
getVirtualIndexes(): Array<number>;
25
calculateRange(): { startIndex: number; endIndex: number } | null;
26
27
// Measurement and sizing
28
measureElement(node: TItemElement | null | undefined): void;
29
resizeItem(index: number, size: number): void;
30
getTotalSize(): number;
31
measure(): void;
32
33
// Scroll control
34
scrollToIndex(index: number, options?: ScrollToIndexOptions): void;
35
scrollToOffset(toOffset: number, options?: ScrollToOffsetOptions): void;
36
scrollBy(delta: number, options?: ScrollToOffsetOptions): void;
37
38
// Position calculations
39
getOffsetForIndex(index: number, align?: ScrollAlignment): [number, ScrollAlignment] | undefined;
40
getOffsetForAlignment(toOffset: number, align: ScrollAlignment, itemSize?: number): number;
41
getVirtualItemForOffset(offset: number): VirtualItem | undefined;
42
43
// Element utilities
44
indexFromElement(node: TItemElement): number;
45
}
46
```
47
48
### Virtualizer Properties
49
50
The Virtualizer instance exposes several readonly properties for accessing current state:
51
52
```typescript { .api }
53
interface VirtualizerState {
54
/** The current scroll element being observed */
55
scrollElement: TScrollElement | null;
56
/** Reference to the target window object */
57
targetWindow: (Window & typeof globalThis) | null;
58
/** Whether the virtualizer is currently scrolling */
59
isScrolling: boolean;
60
/** Cache of measured virtual items */
61
measurementsCache: Array<VirtualItem>;
62
/** Current dimensions of the scroll container */
63
scrollRect: Rect | null;
64
/** Current scroll offset position */
65
scrollOffset: number | null;
66
/** Current scroll direction when scrolling */
67
scrollDirection: ScrollDirection | null;
68
/** Cache of DOM element references */
69
elementsCache: Map<Key, TItemElement>;
70
/** Current visible range of items */
71
range: { startIndex: number; endIndex: number } | null;
72
}
73
```
74
75
### Virtual Item Management
76
77
Methods for managing and accessing virtualized items:
78
79
```typescript { .api }
80
/**
81
* Get the current list of virtual items that should be rendered
82
* @returns Array of VirtualItem objects representing visible items
83
*/
84
getVirtualItems(): Array<VirtualItem>;
85
86
/**
87
* Get the indexes of items that need to be rendered (including overscan)
88
* @returns Array of item indexes to render
89
*/
90
getVirtualIndexes(): Array<number>;
91
92
/**
93
* Calculate the currently visible range of items
94
* @returns Object with startIndex and endIndex, or null if no items visible
95
*/
96
calculateRange(): { startIndex: number; endIndex: number } | null;
97
```
98
99
**Usage Examples:**
100
101
```typescript
102
import { Virtualizer } from '@tanstack/react-virtual';
103
104
// Create virtualizer instance
105
const virtualizer = new Virtualizer({
106
count: 1000,
107
getScrollElement: () => document.getElementById('scroll-container'),
108
estimateSize: () => 50,
109
scrollToFn: (offset, { behavior }, instance) => {
110
instance.scrollElement?.scrollTo({ top: offset, behavior });
111
},
112
observeElementRect: (instance, cb) => {
113
// ResizeObserver implementation
114
const observer = new ResizeObserver(entries => {
115
const entry = entries[0];
116
if (entry) {
117
cb({ width: entry.contentRect.width, height: entry.contentRect.height });
118
}
119
});
120
if (instance.scrollElement) observer.observe(instance.scrollElement as Element);
121
return () => observer.disconnect();
122
},
123
observeElementOffset: (instance, cb) => {
124
// Scroll event implementation
125
const handler = () => cb((instance.scrollElement as Element).scrollTop, true);
126
instance.scrollElement?.addEventListener('scroll', handler);
127
return () => instance.scrollElement?.removeEventListener('scroll', handler);
128
}
129
});
130
131
// Get items to render
132
const virtualItems = virtualizer.getVirtualItems();
133
virtualItems.forEach(item => {
134
console.log(`Render item ${item.index} at position ${item.start}`);
135
});
136
```
137
138
### Measurement and Sizing
139
140
Methods for handling dynamic item sizing and measurement:
141
142
```typescript { .api }
143
/**
144
* Measure a DOM element and update the virtualizer's size cache
145
* @param node - The DOM element to measure, or null to clean up disconnected elements
146
*/
147
measureElement(node: TItemElement | null | undefined): void;
148
149
/**
150
* Manually resize a specific item by index
151
* @param index - Index of the item to resize
152
* @param size - New size of the item in pixels
153
*/
154
resizeItem(index: number, size: number): void;
155
156
/**
157
* Get the total size of all virtualized content
158
* @returns Total height (vertical) or width (horizontal) in pixels
159
*/
160
getTotalSize(): number;
161
162
/**
163
* Force a complete remeasurement of all items
164
* Clears the size cache and triggers recalculation
165
*/
166
measure(): void;
167
```
168
169
**Usage Examples:**
170
171
```typescript
172
// Measure element during render
173
function VirtualItem({ virtualizer, item, data }) {
174
return (
175
<div
176
data-index={item.index}
177
ref={(node) => virtualizer.measureElement(node)}
178
style={{
179
position: 'absolute',
180
top: 0,
181
left: 0,
182
width: '100%',
183
transform: `translateY(${item.start}px)`,
184
}}
185
>
186
{data[item.index]}
187
</div>
188
);
189
}
190
191
// Manual resizing after content change
192
function updateItemContent(virtualizer: Virtualizer, index: number, newContent: string) {
193
// Update content...
194
195
// If we know the new size, update it directly
196
const newSize = estimateContentHeight(newContent);
197
virtualizer.resizeItem(index, newSize);
198
199
// Or trigger complete remeasurement
200
virtualizer.measure();
201
}
202
203
// Get container height for styling
204
function VirtualContainer({ virtualizer }) {
205
const totalSize = virtualizer.getTotalSize();
206
207
return (
208
<div style={{
209
height: `${totalSize}px`,
210
position: 'relative',
211
}}>
212
{/* Virtual items */}
213
</div>
214
);
215
}
216
```
217
218
### Scroll Control
219
220
Methods for programmatically controlling scroll position:
221
222
```typescript { .api }
223
/**
224
* Scroll to a specific item index
225
* @param index - Index of the item to scroll to
226
* @param options - Scroll behavior options
227
*/
228
scrollToIndex(index: number, options?: ScrollToIndexOptions): void;
229
230
/**
231
* Scroll to a specific offset position
232
* @param toOffset - Target scroll position in pixels
233
* @param options - Scroll behavior options
234
*/
235
scrollToOffset(toOffset: number, options?: ScrollToOffsetOptions): void;
236
237
/**
238
* Scroll by a relative amount
239
* @param delta - Amount to scroll in pixels (positive = down/right, negative = up/left)
240
* @param options - Scroll behavior options
241
*/
242
scrollBy(delta: number, options?: ScrollToOffsetOptions): void;
243
244
interface ScrollToIndexOptions {
245
/** How to align the item within the viewport */
246
align?: ScrollAlignment;
247
/** Scroll behavior (smooth or instant) */
248
behavior?: ScrollBehavior;
249
}
250
251
interface ScrollToOffsetOptions {
252
/** How to align the offset within the viewport */
253
align?: ScrollAlignment;
254
/** Scroll behavior (smooth or instant) */
255
behavior?: ScrollBehavior;
256
}
257
258
type ScrollAlignment = 'start' | 'center' | 'end' | 'auto';
259
type ScrollBehavior = 'auto' | 'smooth';
260
```
261
262
**Usage Examples:**
263
264
```typescript
265
// Scroll to specific items
266
function scrollToTop(virtualizer: Virtualizer) {
267
virtualizer.scrollToIndex(0, { align: 'start' });
268
}
269
270
function scrollToBottom(virtualizer: Virtualizer) {
271
virtualizer.scrollToIndex(virtualizer.options.count - 1, { align: 'end' });
272
}
273
274
function scrollToMiddle(virtualizer: Virtualizer) {
275
const middleIndex = Math.floor(virtualizer.options.count / 2);
276
virtualizer.scrollToIndex(middleIndex, { align: 'center', behavior: 'smooth' });
277
}
278
279
// Scroll by relative amounts
280
function scrollPage(virtualizer: Virtualizer, direction: 'up' | 'down') {
281
const pageSize = virtualizer.scrollRect?.height ?? 400;
282
const delta = direction === 'down' ? pageSize : -pageSize;
283
virtualizer.scrollBy(delta, { behavior: 'smooth' });
284
}
285
286
// Scroll to specific positions
287
function scrollToPosition(virtualizer: Virtualizer, position: number) {
288
virtualizer.scrollToOffset(position, { align: 'start' });
289
}
290
```
291
292
### Position Calculations
293
294
Methods for calculating positions and offsets:
295
296
```typescript { .api }
297
/**
298
* Get the scroll offset needed to show a specific item
299
* @param index - Index of the item
300
* @param align - How to align the item (default: 'auto')
301
* @returns Tuple of [offset, actualAlignment] or undefined if item not found
302
*/
303
getOffsetForIndex(index: number, align?: ScrollAlignment): [number, ScrollAlignment] | undefined;
304
305
/**
306
* Calculate the aligned offset for a given position
307
* @param toOffset - Target offset position
308
* @param align - Alignment mode
309
* @param itemSize - Size of the item being aligned (optional)
310
* @returns Calculated offset position
311
*/
312
getOffsetForAlignment(toOffset: number, align: ScrollAlignment, itemSize?: number): number;
313
314
/**
315
* Find the virtual item at a specific scroll offset
316
* @param offset - Scroll offset position
317
* @returns VirtualItem at that position, or undefined if none found
318
*/
319
getVirtualItemForOffset(offset: number): VirtualItem | undefined;
320
```
321
322
### Element Utilities
323
324
Utility methods for working with DOM elements:
325
326
```typescript { .api }
327
/**
328
* Extract the item index from a DOM element's data-index attribute
329
* @param node - The DOM element to inspect
330
* @returns The item index, or -1 if not found or invalid
331
*/
332
indexFromElement(node: TItemElement): number;
333
```
334
335
**Usage Examples:**
336
337
```typescript
338
// Handle click events on virtual items
339
function handleItemClick(event: MouseEvent, virtualizer: Virtualizer) {
340
const element = event.target as HTMLElement;
341
const index = virtualizer.indexFromElement(element);
342
if (index >= 0) {
343
console.log(`Clicked item at index ${index}`);
344
}
345
}
346
347
// Find item at current scroll position
348
function getCurrentItem(virtualizer: Virtualizer) {
349
const currentOffset = virtualizer.scrollOffset ?? 0;
350
const item = virtualizer.getVirtualItemForOffset(currentOffset);
351
return item;
352
}
353
354
// Calculate positions for custom scroll animations
355
function animateToItem(virtualizer: Virtualizer, targetIndex: number) {
356
const offsetInfo = virtualizer.getOffsetForIndex(targetIndex, 'center');
357
if (offsetInfo) {
358
const [targetOffset] = offsetInfo;
359
// Implement custom animation to targetOffset
360
animateScrollTo(targetOffset);
361
}
362
}
363
```
364
365
## Configuration and Options
366
367
The Virtualizer requires comprehensive configuration through `VirtualizerOptions`:
368
369
```typescript { .api }
370
export interface VirtualizerOptions<TScrollElement extends Element | Window, TItemElement extends Element> {
371
// Required configuration
372
count: number;
373
getScrollElement: () => TScrollElement | null;
374
estimateSize: (index: number) => number;
375
scrollToFn: (offset: number, options: ScrollOptions, instance: Virtualizer<TScrollElement, TItemElement>) => void;
376
observeElementRect: (instance: Virtualizer<TScrollElement, TItemElement>, cb: (rect: Rect) => void) => void | (() => void);
377
observeElementOffset: (instance: Virtualizer<TScrollElement, TItemElement>, cb: (offset: number, isScrolling: boolean) => void) => void | (() => void);
378
379
// Optional configuration with defaults
380
debug?: boolean; // false
381
initialRect?: Rect; // { width: 0, height: 0 }
382
onChange?: (instance: Virtualizer<TScrollElement, TItemElement>, sync: boolean) => void;
383
measureElement?: (element: TItemElement, entry: ResizeObserverEntry | undefined, instance: Virtualizer<TScrollElement, TItemElement>) => number;
384
overscan?: number; // 1
385
horizontal?: boolean; // false
386
paddingStart?: number; // 0
387
paddingEnd?: number; // 0
388
scrollPaddingStart?: number; // 0
389
scrollPaddingEnd?: number; // 0
390
initialOffset?: number | (() => number); // 0
391
getItemKey?: (index: number) => Key; // (index) => index
392
rangeExtractor?: (range: Range) => Array<number>; // defaultRangeExtractor
393
scrollMargin?: number; // 0
394
gap?: number; // 0
395
indexAttribute?: string; // 'data-index'
396
initialMeasurementsCache?: Array<VirtualItem>; // []
397
lanes?: number; // 1
398
isScrollingResetDelay?: number; // 150
399
useScrollendEvent?: boolean; // false
400
enabled?: boolean; // true
401
isRtl?: boolean; // false
402
useAnimationFrameWithResizeObserver?: boolean; // false
403
}
404
405
export interface ScrollOptions {
406
adjustments?: number;
407
behavior?: ScrollBehavior;
408
}
409
```
410
411
## Performance Considerations
412
413
### Efficient Range Calculation
414
415
The Virtualizer uses binary search algorithms to efficiently calculate visible ranges even with thousands of items.
416
417
### Memory Management
418
419
- Automatically cleans up disconnected DOM elements
420
- Manages ResizeObserver connections efficiently
421
- Caches measurements to avoid unnecessary calculations
422
423
### Scroll Performance
424
425
- Debounced scroll end detection
426
- Optimized for both mouse wheel and touch scrolling
427
- Support for native `scrollend` events where available