0
# List Virtualization
1
2
Core list virtualization component that efficiently renders large datasets by only displaying visible items plus a configurable buffer. Automatically handles variable-sized items without manual measurements.
3
4
## Capabilities
5
6
### Virtuoso Component
7
8
Main virtualization component for rendering lists with automatic size detection and scroll optimization.
9
10
```typescript { .api }
11
/**
12
* Main virtualization component for rendering lists with automatic size detection
13
* @param props - Configuration options for the virtualized list
14
* @returns JSX.Element representing the virtualized list
15
*/
16
function Virtuoso<D = any, C = any>(props: VirtuosoProps<D, C>): JSX.Element;
17
18
interface VirtuosoProps<D, C> extends ListRootProps {
19
/** The data items to be rendered. If data is set, totalCount will be inferred from the length */
20
data?: readonly D[];
21
/** The total amount of items to be rendered */
22
totalCount?: number;
23
/** Set the callback to specify the contents of each item */
24
itemContent?: ItemContent<D, C>;
25
/** Use the components property for advanced customization of rendered elements */
26
components?: Components<D, C>;
27
/** Additional context available in custom components and content callbacks */
28
context?: C;
29
/** If specified, the component will use the function to generate the key property for each list item */
30
computeItemKey?: ComputeItemKey<D, C>;
31
32
/** Can be used to improve performance if the rendered items are of known size */
33
fixedItemHeight?: number;
34
/** By default, the component assumes the default item height from the first rendered item */
35
defaultItemHeight?: number;
36
37
/** If set to true, the list automatically scrolls to bottom if the total count is changed */
38
followOutput?: FollowOutput;
39
/** Gets called when the user scrolls to the end of the list */
40
endReached?: (index: number) => void;
41
/** Called when the user scrolls to the start of the list */
42
startReached?: (index: number) => void;
43
44
/** Called with the new set of items each time the list items are rendered due to scrolling */
45
rangeChanged?: (range: ListRange) => void;
46
/** Called with the new set of items each time the list items are rendered due to scrolling */
47
itemsRendered?: (items: ListItem<D>[]) => void;
48
49
/** Called with true / false when the list has reached the bottom / gets scrolled up */
50
atBottomStateChange?: (atBottom: boolean) => void;
51
/** Called with true / false when the list has reached the top / gets scrolled down */
52
atTopStateChange?: (atTop: boolean) => void;
53
54
/** Use when implementing inverse infinite scrolling */
55
firstItemIndex?: number;
56
/** Set to a value between 0 and totalCount - 1 to make the list start scrolled to that item */
57
initialTopMostItemIndex?: IndexLocationWithAlign | number;
58
/** Set this value to offset the initial location of the list */
59
initialScrollTop?: number;
60
61
/** Setting alignToBottom to true aligns the items to the bottom of the list if shorter than viewport */
62
alignToBottom?: boolean;
63
/** Uses the document scroller rather than wrapping the list in its own */
64
useWindowScroll?: boolean;
65
/** Pass a reference to a scrollable parent element */
66
customScrollParent?: HTMLElement;
67
68
/** Use to display placeholders if the user scrolls fast through the list */
69
scrollSeekConfiguration?: false | ScrollSeekConfiguration;
70
/** Set the overscan property to make the component chunk the rendering of new items on scroll */
71
overscan?: number | { main: number; reverse: number };
72
/** Set the increaseViewportBy property to artificially increase the viewport size */
73
increaseViewportBy?: number | { top: number; bottom: number };
74
75
/** Called when the list starts/stops scrolling */
76
isScrolling?: (isScrolling: boolean) => void;
77
/** Provides access to the root DOM element */
78
scrollerRef?: (ref: HTMLElement | null | Window) => any;
79
/** Pass a state obtained from getState() method to restore the list state */
80
restoreStateFrom?: StateSnapshot;
81
82
/** Set the amount of items to remain fixed at the top of the list */
83
topItemCount?: number;
84
/** Called when the total list height is changed due to new items or viewport resize */
85
totalListHeightChanged?: (height: number) => void;
86
/** Allows customizing the height/width calculation of Item elements */
87
itemSize?: SizeFunction;
88
/** Use for server-side rendering - if set, the list will render the specified amount of items */
89
initialItemCount?: number;
90
/** When set, turns the scroller into a horizontal list */
91
horizontalDirection?: boolean;
92
93
/** Implement this callback to adjust list position when total count changes */
94
scrollIntoViewOnChange?: (params: {
95
context: C;
96
totalCount: number;
97
scrollingInProgress: boolean;
98
}) => ScrollIntoViewLocation | null | undefined | false | void;
99
100
/** Set to customize the wrapper tag for header and footer components (default is 'div') */
101
headerFooterTag?: string;
102
103
/** Set to LogLevel.DEBUG to enable various diagnostics in the console */
104
logLevel?: LogLevel;
105
/** By default 4. Redefine to change how much away from the bottom the scroller can be */
106
atBottomThreshold?: number;
107
/** By default 0. Redefine to change how much away from the top the scroller can be */
108
atTopThreshold?: number;
109
/** When set, the resize observer will not use requestAnimationFrame to report size changes */
110
skipAnimationFrameInResizeObserver?: boolean;
111
}
112
113
type ItemContent<D, C> = (index: number, data: D, context: C) => React.ReactNode;
114
type ComputeItemKey<D, C> = (index: number, item: D, context: C) => React.Key;
115
type ListRootProps = Omit<React.HTMLProps<HTMLDivElement>, 'data' | 'ref'>;
116
117
type ScrollIntoViewLocation = FlatScrollIntoViewLocation | GroupedScrollIntoViewLocation;
118
119
interface FlatScrollIntoViewLocation extends ScrollIntoViewLocationOptions {
120
index: number;
121
}
122
123
interface GroupedScrollIntoViewLocation extends ScrollIntoViewLocationOptions {
124
groupIndex: number;
125
}
126
127
interface ScrollIntoViewLocationOptions {
128
align?: 'center' | 'end' | 'start';
129
behavior?: 'auto' | 'smooth';
130
calculateViewLocation?: CalculateViewLocation;
131
done?: () => void;
132
}
133
```
134
135
**Usage Examples:**
136
137
```typescript
138
import React from 'react';
139
import { Virtuoso } from 'react-virtuoso';
140
141
// Basic list with data array
142
function BasicList() {
143
const items = Array.from({ length: 10000 }, (_, i) => ({
144
id: i,
145
name: `Item ${i}`,
146
value: Math.random()
147
}));
148
149
return (
150
<Virtuoso
151
style={{ height: '400px' }}
152
data={items}
153
itemContent={(index, item) => (
154
<div style={{ padding: '12px', borderBottom: '1px solid #eee' }}>
155
<strong>{item.name}</strong>
156
<div>Value: {item.value.toFixed(3)}</div>
157
</div>
158
)}
159
/>
160
);
161
}
162
163
// List with totalCount (without data array)
164
function CountBasedList() {
165
return (
166
<Virtuoso
167
style={{ height: '400px' }}
168
totalCount={100000}
169
itemContent={(index) => (
170
<div style={{ padding: '12px' }}>
171
Item {index}
172
</div>
173
)}
174
/>
175
);
176
}
177
178
// List with infinite scrolling
179
function InfiniteList() {
180
const [items, setItems] = React.useState(
181
Array.from({ length: 100 }, (_, i) => `Item ${i}`)
182
);
183
184
const loadMore = () => {
185
setItems(prev => [
186
...prev,
187
...Array.from({ length: 50 }, (_, i) => `Item ${prev.length + i}`)
188
]);
189
};
190
191
return (
192
<Virtuoso
193
style={{ height: '400px' }}
194
data={items}
195
endReached={loadMore}
196
itemContent={(index, item) => (
197
<div style={{ padding: '12px' }}>
198
{item}
199
</div>
200
)}
201
/>
202
);
203
}
204
205
// List with custom components
206
function CustomComponentsList() {
207
const items = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
208
209
return (
210
<Virtuoso
211
style={{ height: '400px' }}
212
data={items}
213
components={{
214
Header: () => <div style={{ padding: '12px', fontWeight: 'bold' }}>Header</div>,
215
Footer: () => <div style={{ padding: '12px', fontWeight: 'bold' }}>Footer</div>,
216
EmptyPlaceholder: () => <div>No items to display</div>,
217
Item: ({ children, ...props }) => (
218
<div {...props} style={{ padding: '8px', margin: '4px', backgroundColor: '#f5f5f5' }}>
219
{children}
220
</div>
221
)
222
}}
223
itemContent={(index, item) => item}
224
/>
225
);
226
}
227
```
228
229
### Follow Output
230
231
Automatically scroll to bottom when new items are added, perfect for chat interfaces and live feeds.
232
233
```typescript { .api }
234
/**
235
* Configure automatic scrolling behavior when new items are added
236
*/
237
type FollowOutput = FollowOutputCallback | FollowOutputScalarType;
238
type FollowOutputCallback = (isAtBottom: boolean) => FollowOutputScalarType;
239
type FollowOutputScalarType = 'auto' | 'smooth' | boolean;
240
```
241
242
**Usage Example:**
243
244
```typescript
245
// Always follow output with smooth scrolling
246
<Virtuoso
247
followOutput="smooth"
248
data={messages}
249
itemContent={(index, message) => <div>{message.text}</div>}
250
/>
251
252
// Conditional follow output
253
<Virtuoso
254
followOutput={(isAtBottom) => {
255
// Only auto-scroll if user is already at bottom
256
return isAtBottom ? 'smooth' : false;
257
}}
258
data={messages}
259
itemContent={(index, message) => <div>{message.text}</div>}
260
/>
261
```
262
263
### Scroll Seek Configuration
264
265
Display placeholders during fast scrolling to improve performance.
266
267
```typescript { .api }
268
interface ScrollSeekConfiguration {
269
/** Callback to determine if the list should enter scroll seek mode */
270
enter: ScrollSeekToggle;
271
/** Callback to determine if the list should exit scroll seek mode */
272
exit: ScrollSeekToggle;
273
/** Called during scrolling in scroll seek mode - use to display a hint where the list is */
274
change?: (velocity: number, range: ListRange) => void;
275
}
276
277
type ScrollSeekToggle = (velocity: number, range: ListRange) => boolean;
278
279
interface ScrollSeekPlaceholderProps {
280
height: number;
281
index: number;
282
type: 'group' | 'item';
283
groupIndex?: number;
284
}
285
```
286
287
**Usage Example:**
288
289
```typescript
290
<Virtuoso
291
scrollSeekConfiguration={{
292
enter: (velocity) => Math.abs(velocity) > 200,
293
exit: (velocity) => Math.abs(velocity) < 30,
294
}}
295
components={{
296
ScrollSeekPlaceholder: ({ height, index }) => (
297
<div style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
298
Loading item {index}...
299
</div>
300
)
301
}}
302
data={items}
303
itemContent={(index, item) => <div>{item}</div>}
304
/>
305
```
306
307
### Performance Optimization
308
309
```typescript { .api }
310
/** Calculates the height of el, which will be the Item element in the DOM */
311
type SizeFunction = (el: HTMLElement, field: 'offsetHeight' | 'offsetWidth') => number;
312
313
interface StateSnapshot {
314
ranges: SizeRange[];
315
scrollTop: number;
316
}
317
318
interface SizeRange {
319
startIndex: number;
320
endIndex: number;
321
size: number;
322
}
323
324
type StateCallback = (state: StateSnapshot) => void;
325
```
326
327
**Usage Example:**
328
329
```typescript
330
function OptimizedList() {
331
const [state, setState] = React.useState<StateSnapshot | null>(null);
332
333
return (
334
<Virtuoso
335
// Fixed height for better performance
336
fixedItemHeight={50}
337
// Reduce overscan for memory efficiency
338
overscan={{ main: 5, reverse: 5 }}
339
// Restore previous state
340
restoreStateFrom={state}
341
// Custom size calculation
342
itemSize={(el) => el.getBoundingClientRect().height}
343
data={items}
344
itemContent={(index, item) => <div>{item}</div>}
345
/>
346
);
347
}
348
```
349
350
## Types
351
352
```typescript { .api }
353
interface ListItem<D> {
354
data?: D;
355
index: number;
356
offset: number;
357
size: number;
358
}
359
360
interface ListRange {
361
startIndex: number;
362
endIndex: number;
363
}
364
365
interface Item<D> {
366
data?: D;
367
index: number;
368
offset: number;
369
size: number;
370
}
371
372
enum LogLevel {
373
DEBUG,
374
INFO,
375
WARN,
376
ERROR
377
}
378
379
interface LocationOptions {
380
align?: 'center' | 'end' | 'start';
381
behavior?: 'auto' | 'smooth';
382
offset?: number;
383
}
384
385
interface FlatIndexLocationWithAlign extends LocationOptions {
386
index: 'LAST' | number;
387
}
388
389
type IndexLocationWithAlign = FlatIndexLocationWithAlign;
390
```