0
# Component Handles
1
2
All React Virtuoso components provide handle interfaces for programmatic control including scrolling operations, state management, and viewport queries. These handles are accessed through React refs and provide methods for external control of virtualization behavior.
3
4
## Capabilities
5
6
### VirtuosoHandle
7
8
Handle interface for the main Virtuoso list component providing comprehensive scroll control and state management.
9
10
```typescript { .api }
11
interface VirtuosoHandle {
12
/** Use this with combination with follow output if you have images loading in the list */
13
autoscrollToBottom(): void;
14
/** Obtains the internal size state of the component, so that it can be restored later */
15
getState(stateCb: StateCallback): void;
16
/** Scrolls the component with the specified amount */
17
scrollBy(location: ScrollToOptions): void;
18
/** Scrolls the item into view if necessary */
19
scrollIntoView(location: FlatScrollIntoViewLocation): void;
20
/** Scrolls the component to the specified location */
21
scrollTo(location: ScrollToOptions): void;
22
/** Scrolls the component to the specified item index */
23
scrollToIndex(location: FlatIndexLocationWithAlign | number): void;
24
}
25
26
interface FlatScrollIntoViewLocation extends ScrollIntoViewLocationOptions {
27
index: number;
28
}
29
30
interface FlatIndexLocationWithAlign extends LocationOptions {
31
index: 'LAST' | number;
32
}
33
34
interface LocationOptions {
35
align?: 'center' | 'end' | 'start';
36
behavior?: 'auto' | 'smooth';
37
offset?: number;
38
}
39
40
interface ScrollIntoViewLocationOptions {
41
align?: 'center' | 'end' | 'start';
42
behavior?: 'auto' | 'smooth';
43
calculateViewLocation?: CalculateViewLocation;
44
done?: () => void;
45
}
46
47
type StateCallback = (state: StateSnapshot) => void;
48
49
interface StateSnapshot {
50
ranges: SizeRange[];
51
scrollTop: number;
52
}
53
```
54
55
**Usage Examples:**
56
57
```typescript
58
import React from 'react';
59
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
60
61
function ControlledList() {
62
const virtuosoRef = React.useRef<VirtuosoHandle>(null);
63
const [items] = React.useState(
64
Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
65
);
66
67
// Scroll to specific index
68
const scrollToIndex = (index: number) => {
69
virtuosoRef.current?.scrollToIndex(index);
70
};
71
72
// Smooth scroll to index with alignment
73
const smoothScrollToIndex = (index: number) => {
74
virtuosoRef.current?.scrollToIndex({
75
index,
76
align: 'center',
77
behavior: 'smooth'
78
});
79
};
80
81
// Scroll to last item
82
const scrollToEnd = () => {
83
virtuosoRef.current?.scrollToIndex('LAST');
84
};
85
86
// Smart scroll into view
87
const scrollIntoView = (index: number) => {
88
virtuosoRef.current?.scrollIntoView({
89
index,
90
behavior: 'smooth',
91
done: () => console.log('Scroll completed')
92
});
93
};
94
95
// Force scroll to bottom (useful for chat)
96
const forceScrollToBottom = () => {
97
virtuosoRef.current?.autoscrollToBottom();
98
};
99
100
// Scroll by amount
101
const scrollByAmount = (pixels: number) => {
102
virtuosoRef.current?.scrollBy({ top: pixels, behavior: 'smooth' });
103
};
104
105
// Scroll to absolute position
106
const scrollToPosition = (scrollTop: number) => {
107
virtuosoRef.current?.scrollTo({ top: scrollTop });
108
};
109
110
// Get and restore state
111
const [savedState, setSavedState] = React.useState<StateSnapshot | null>(null);
112
113
const saveState = () => {
114
virtuosoRef.current?.getState(setSavedState);
115
};
116
117
return (
118
<div>
119
<div style={{ marginBottom: '10px' }}>
120
<button onClick={() => scrollToIndex(0)}>Go to Start</button>
121
<button onClick={() => smoothScrollToIndex(500)}>Go to Middle</button>
122
<button onClick={scrollToEnd}>Go to End</button>
123
<button onClick={() => scrollIntoView(1000)}>Smart Scroll to 1000</button>
124
<button onClick={forceScrollToBottom}>Force Bottom</button>
125
<button onClick={() => scrollByAmount(200)}>Scroll Down 200px</button>
126
<button onClick={() => scrollToPosition(0)}>Reset Position</button>
127
<button onClick={saveState}>Save State</button>
128
</div>
129
130
<Virtuoso
131
ref={virtuosoRef}
132
style={{ height: '400px' }}
133
data={items}
134
restoreStateFrom={savedState}
135
itemContent={(index, item) => (
136
<div style={{ padding: '12px', borderBottom: '1px solid #eee' }}>
137
{item} (Index: {index})
138
</div>
139
)}
140
/>
141
</div>
142
);
143
}
144
```
145
146
### GroupedVirtuosoHandle
147
148
Handle interface for the GroupedVirtuoso component with additional group-aware scrolling capabilities.
149
150
```typescript { .api }
151
interface GroupedVirtuosoHandle {
152
/** Use this with combination with follow output if you have images loading in the list */
153
autoscrollToBottom(): void;
154
/** Obtains the internal size state of the component for restoration */
155
getState(stateCb: StateCallback): void;
156
/** Scrolls the component with the specified amount */
157
scrollBy(location: ScrollToOptions): void;
158
/** Scrolls the item into view if necessary with group context */
159
scrollIntoView(location: number | ScrollIntoViewLocation): void;
160
/** Scrolls the component to the specified location */
161
scrollTo(location: ScrollToOptions): void;
162
/** Scrolls the component to the specified item or group index */
163
scrollToIndex(location: IndexLocationWithAlign | number): void;
164
}
165
166
type IndexLocationWithAlign = FlatIndexLocationWithAlign | GroupIndexLocationWithAlign;
167
168
interface GroupIndexLocationWithAlign extends LocationOptions {
169
groupIndex: number;
170
}
171
172
type ScrollIntoViewLocation = FlatScrollIntoViewLocation | GroupedScrollIntoViewLocation;
173
174
interface GroupedScrollIntoViewLocation extends ScrollIntoViewLocationOptions {
175
groupIndex: number;
176
}
177
```
178
179
**Usage Example:**
180
181
```typescript
182
import React from 'react';
183
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso';
184
185
function ControlledGroupedList() {
186
const groupedRef = React.useRef<GroupedVirtuosoHandle>(null);
187
188
const groups = [
189
{ name: 'Group A', items: Array.from({ length: 50 }, (_, i) => `A-${i}`) },
190
{ name: 'Group B', items: Array.from({ length: 30 }, (_, i) => `B-${i}`) },
191
{ name: 'Group C', items: Array.from({ length: 40 }, (_, i) => `C-${i}`) }
192
];
193
194
const groupCounts = groups.map(g => g.items.length);
195
const allItems = groups.flatMap(g => g.items);
196
197
// Scroll to specific group
198
const scrollToGroup = (groupIndex: number) => {
199
groupedRef.current?.scrollToIndex({
200
groupIndex,
201
align: 'start',
202
behavior: 'smooth'
203
});
204
};
205
206
// Scroll to specific item by flat index
207
const scrollToItem = (itemIndex: number) => {
208
groupedRef.current?.scrollToIndex({
209
index: itemIndex,
210
align: 'center'
211
});
212
};
213
214
// Scroll group into view
215
const scrollGroupIntoView = (groupIndex: number) => {
216
groupedRef.current?.scrollIntoView({
217
groupIndex,
218
behavior: 'smooth'
219
});
220
};
221
222
return (
223
<div>
224
<div style={{ marginBottom: '10px' }}>
225
{groups.map((group, index) => (
226
<button key={index} onClick={() => scrollToGroup(index)}>
227
Go to {group.name}
228
</button>
229
))}
230
<button onClick={() => scrollToItem(50)}>Go to Item 50</button>
231
</div>
232
233
<GroupedVirtuoso
234
ref={groupedRef}
235
style={{ height: '400px' }}
236
groupCounts={groupCounts}
237
groupContent={(index) => (
238
<div style={{
239
padding: '12px',
240
backgroundColor: '#f0f0f0',
241
fontWeight: 'bold'
242
}}>
243
{groups[index].name}
244
</div>
245
)}
246
itemContent={(index, groupIndex, item) => (
247
<div style={{ padding: '8px 16px', borderBottom: '1px solid #eee' }}>
248
{item} (Group: {groupIndex})
249
</div>
250
)}
251
data={allItems}
252
/>
253
</div>
254
);
255
}
256
```
257
258
### VirtuosoGridHandle
259
260
Handle interface for the VirtuosoGrid component providing grid-specific scrolling capabilities.
261
262
```typescript { .api }
263
interface VirtuosoGridHandle {
264
/** Scrolls the grid with the specified amount */
265
scrollBy(location: ScrollToOptions): void;
266
/** Scrolls the grid to the specified location */
267
scrollTo(location: ScrollToOptions): void;
268
/** Scrolls the grid to the specified item index */
269
scrollToIndex(location: GridIndexLocation): void;
270
}
271
272
type GridIndexLocation = FlatIndexLocationWithAlign | number;
273
```
274
275
**Usage Example:**
276
277
```typescript
278
import React from 'react';
279
import { VirtuosoGrid, VirtuosoGridHandle } from 'react-virtuoso';
280
281
function ControlledGrid() {
282
const gridRef = React.useRef<VirtuosoGridHandle>(null);
283
const items = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
284
285
const scrollToItem = (index: number) => {
286
gridRef.current?.scrollToIndex({
287
index,
288
align: 'start',
289
behavior: 'smooth'
290
});
291
};
292
293
const jumpToTop = () => {
294
gridRef.current?.scrollTo({ top: 0, behavior: 'smooth' });
295
};
296
297
const scrollDown = () => {
298
gridRef.current?.scrollBy({ top: 300, behavior: 'smooth' });
299
};
300
301
return (
302
<div>
303
<div style={{ marginBottom: '10px' }}>
304
<button onClick={jumpToTop}>Top</button>
305
<button onClick={() => scrollToItem(100)}>Item 100</button>
306
<button onClick={() => scrollToItem(500)}>Item 500</button>
307
<button onClick={scrollDown}>Scroll Down</button>
308
</div>
309
310
<VirtuosoGrid
311
ref={gridRef}
312
style={{ height: '400px' }}
313
data={items}
314
itemContent={(index, item) => (
315
<div style={{
316
padding: '16px',
317
border: '1px solid #ddd',
318
margin: '4px',
319
textAlign: 'center'
320
}}>
321
{item}
322
</div>
323
)}
324
/>
325
</div>
326
);
327
}
328
```
329
330
### TableVirtuosoHandle
331
332
Handle interface for table virtualization components.
333
334
```typescript { .api }
335
interface TableVirtuosoHandle {
336
/** Obtains the internal size state of the component for restoration */
337
getState(stateCb: StateCallback): void;
338
/** Scrolls the table with the specified amount */
339
scrollBy(location: ScrollToOptions): void;
340
/** Scrolls the table to the specified location */
341
scrollTo(location: ScrollToOptions): void;
342
/** Scrolls the row into view if necessary */
343
scrollIntoView(location: FlatScrollIntoViewLocation | number): void;
344
/** Scrolls the table to the specified row index */
345
scrollToIndex(location: FlatIndexLocationWithAlign | number): void;
346
}
347
348
interface GroupedTableVirtuosoHandle {
349
/** Obtains the internal size state of the component for restoration */
350
getState(stateCb: StateCallback): void;
351
/** Scrolls the table with the specified amount */
352
scrollBy(location: ScrollToOptions): void;
353
/** Scrolls the table to the specified location */
354
scrollTo(location: ScrollToOptions): void;
355
/** Scrolls the item into view if necessary with group context */
356
scrollIntoView(location: ScrollIntoViewLocationOptions): void;
357
/** Scrolls the table to the specified item index */
358
scrollToIndex(location: IndexLocationWithAlign | number): void;
359
}
360
```
361
362
**Usage Example:**
363
364
```typescript
365
import React from 'react';
366
import { TableVirtuoso, TableVirtuosoHandle } from 'react-virtuoso';
367
368
function ControlledTable() {
369
const tableRef = React.useRef<TableVirtuosoHandle>(null);
370
const data = Array.from({ length: 1000 }, (_, i) => ({
371
id: i + 1,
372
name: `Row ${i + 1}`,
373
value: Math.random() * 100
374
}));
375
376
const scrollToRow = (index: number) => {
377
tableRef.current?.scrollToIndex({
378
index,
379
align: 'center',
380
behavior: 'smooth'
381
});
382
};
383
384
const scrollRowIntoView = (index: number) => {
385
tableRef.current?.scrollIntoView({
386
index,
387
behavior: 'smooth',
388
done: () => console.log(`Scrolled to row ${index}`)
389
});
390
};
391
392
const saveTableState = () => {
393
tableRef.current?.getState((state) => {
394
console.log('Table state:', state);
395
// Save state to localStorage or state management
396
});
397
};
398
399
return (
400
<div>
401
<div style={{ marginBottom: '10px' }}>
402
<button onClick={() => scrollToRow(0)}>First Row</button>
403
<button onClick={() => scrollToRow(500)}>Middle Row</button>
404
<button onClick={() => scrollRowIntoView(999)}>Last Row</button>
405
<button onClick={saveTableState}>Save State</button>
406
</div>
407
408
<TableVirtuoso
409
ref={tableRef}
410
style={{ height: '400px' }}
411
data={data}
412
fixedHeaderContent={() => (
413
<tr>
414
<th style={{ padding: '12px' }}>ID</th>
415
<th style={{ padding: '12px' }}>Name</th>
416
<th style={{ padding: '12px' }}>Value</th>
417
</tr>
418
)}
419
itemContent={(index, item) => (
420
<>
421
<td style={{ padding: '12px' }}>{item.id}</td>
422
<td style={{ padding: '12px' }}>{item.name}</td>
423
<td style={{ padding: '12px' }}>{item.value.toFixed(2)}</td>
424
</>
425
)}
426
/>
427
</div>
428
);
429
}
430
```
431
432
## Advanced Scroll Control
433
434
### Custom Scroll Calculation
435
436
Fine-tune scroll behavior with custom view location calculation.
437
438
```typescript { .api }
439
type CalculateViewLocation = (params: CalculateViewLocationParams) => IndexLocationWithAlign | null | number;
440
441
interface CalculateViewLocationParams {
442
itemBottom: number;
443
itemTop: number;
444
locationParams: {
445
align?: 'center' | 'end' | 'start';
446
behavior?: 'auto' | 'smooth';
447
} & ({ groupIndex: number } | { index: number });
448
viewportBottom: number;
449
viewportTop: number;
450
}
451
```
452
453
**Usage Example:**
454
455
```typescript
456
// Custom scroll behavior that only scrolls if item is completely out of view
457
const customScrollBehavior: CalculateViewLocation = ({
458
itemTop,
459
itemBottom,
460
viewportTop,
461
viewportBottom,
462
locationParams
463
}) => {
464
// Only scroll if item is completely out of view
465
const completelyAbove = itemBottom < viewportTop;
466
const completelyBelow = itemTop > viewportBottom;
467
468
if (completelyAbove) {
469
return { ...locationParams, align: 'start' };
470
} else if (completelyBelow) {
471
return { ...locationParams, align: 'end' };
472
}
473
474
// Item is at least partially visible, don't scroll
475
return null;
476
};
477
478
// Use in scrollIntoView
479
virtuosoRef.current?.scrollIntoView({
480
index: targetIndex,
481
calculateViewLocation: customScrollBehavior,
482
behavior: 'smooth'
483
});
484
```
485
486
### State Management
487
488
```typescript { .api }
489
interface StateSnapshot {
490
ranges: SizeRange[];
491
scrollTop: number;
492
}
493
494
interface SizeRange {
495
startIndex: number;
496
endIndex: number;
497
size: number;
498
}
499
```
500
501
**Usage Example:**
502
503
```typescript
504
function StatefulVirtuoso() {
505
const [savedState, setSavedState] = React.useState<StateSnapshot | null>(null);
506
const virtuosoRef = React.useRef<VirtuosoHandle>(null);
507
508
// Save state before navigation
509
const handleNavigation = () => {
510
virtuosoRef.current?.getState((state) => {
511
localStorage.setItem('virtuoso-state', JSON.stringify(state));
512
});
513
};
514
515
// Restore state on mount
516
React.useEffect(() => {
517
const stored = localStorage.getItem('virtuoso-state');
518
if (stored) {
519
setSavedState(JSON.parse(stored));
520
}
521
}, []);
522
523
return (
524
<Virtuoso
525
ref={virtuosoRef}
526
restoreStateFrom={savedState}
527
data={items}
528
itemContent={(index, item) => <div>{item}</div>}
529
/>
530
);
531
}
532
```
533
534
## Types
535
536
```typescript { .api }
537
interface ScrollToOptions {
538
top?: number;
539
left?: number;
540
behavior?: 'auto' | 'smooth';
541
}
542
543
enum LogLevel {
544
DEBUG,
545
INFO,
546
WARN,
547
ERROR
548
}
549
```