What is react-virtual?
The react-virtual package is a lightweight and performant library for rendering large lists and tabular data in React applications. It helps in efficiently managing and displaying large datasets by only rendering the visible items, thus improving performance and reducing memory usage.
What are react-virtual's main functionalities?
Virtualized List
This feature allows you to create a virtualized list where only the visible items are rendered. This improves performance when dealing with large datasets.
import { useVirtual } from 'react-virtual';
function VirtualizedList({ items }) {
const parentRef = React.useRef();
const rowVirtualizer = useVirtual({
size: items.length,
parentRef,
estimateSize: React.useCallback(() => 35, []),
});
return (
<div ref={parentRef} style={{ height: `400px`, overflow: 'auto' }}>
<div style={{ height: `${rowVirtualizer.totalSize}px`, width: '100%' }}>
{rowVirtualizer.virtualItems.map(virtualRow => (
<div
key={virtualRow.index}
ref={virtualRow.measureRef}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`
}}
>
{items[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
Virtualized Grid
This feature allows you to create a virtualized grid where only the visible cells are rendered. This is useful for rendering large tables or grids efficiently.
import { useVirtual } from 'react-virtual';
function VirtualizedGrid({ columns, rows }) {
const parentRef = React.useRef();
const columnVirtualizer = useVirtual({
size: columns.length,
parentRef,
estimateSize: React.useCallback(() => 100, []),
horizontal: true,
});
const rowVirtualizer = useVirtual({
size: rows.length,
parentRef,
estimateSize: React.useCallback(() => 35, []),
});
return (
<div ref={parentRef} style={{ height: `400px`, width: `600px`, overflow: 'auto' }}>
<div
style={{
height: `${rowVirtualizer.totalSize}px`,
width: `${columnVirtualizer.totalSize}px`,
position: 'relative',
}}
>
{rowVirtualizer.virtualItems.map(virtualRow => (
<div
key={virtualRow.index}
ref={virtualRow.measureRef}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateY(${virtualRow.start}px)`
}}
>
{columnVirtualizer.virtualItems.map(virtualColumn => (
<div
key={virtualColumn.index}
ref={virtualColumn.measureRef}
style={{
position: 'absolute',
top: 0,
left: 0,
height: '100%',
transform: `translateX(${virtualColumn.start}px)`
}}
>
{rows[virtualRow.index][virtualColumn.index]}
</div>
))}
</div>
))}
</div>
</div>
);
}
Other packages similar to react-virtual
react-window
react-window is a library for rendering large lists and tabular data efficiently. It is similar to react-virtual in that it only renders the visible items, but it is more lightweight and has a simpler API. However, react-virtual offers more flexibility and customization options.
react-virtualized
react-virtualized is another popular library for rendering large lists and tabular data. It offers a wide range of features and components, including grids, lists, tables, and more. Compared to react-virtual, react-virtualized is more feature-rich but also more complex and heavier.

Hooks for virtualizing scrollable elements in React
Enjoy this library? Try them all! React Table, React Virtual, React Form,
React Charts
Quick Features
- 4kb - 6kb (depending on features imported)
Core Issues and Solution
The Challenge
Tools for managing "global state" are plentiful these days, but most of these tools:
- Mistake server cache state for global state
- Force you to manage async data in a synchronous way
- Duplicate unnecessary network operations
- Use naive or over-engineered caching strategies
- Are too basic to handle large-scale apps or
- Are too complex or built for highly-opinionated systems like Redux, GraphQL, [insert proprietary tools], etc.
- Do not provide tools for server mutations
- Either do not provide easy access to the cache or do, but expose overpowered foot-gun APIs to the developer
The Solution
React Virtual exports a set of hooks that address these issues. Out of the box, React Virtual:
- Separates your server cache state from your global state
- Provides async aware APIs for reading and updating server state/cache
- Dedupes both async and sync requests to async resources
- Automatically caches data, invalidates and refetches stale data, and manages garbage collection of unused data
- Scales easily as your application grows
- Is based solely on Promises, making it highly unopinionated and interoperable with any data fetching strategy including REST, GraphQL and other transactional APIs
- Provides an integrated promise-based mutation API
- Opt-in Manual or Advance cache management
Inspiration & Hat-Tipping
A big thanks to both [Draqula](https://github.com/vadimdemedes/draqula) for inspiring a lot of React Virtual's original API and documentation and also [Zeit's SWR](https://github.com/zeit/swr) and its creators for inspiring even further customizations and examples. You all rock!
How is this different from Zeit's SWR?
Zeit's SWR is a great library, and is very similar in spirit and implementation to React Virtual with a few notable differences:
- Automatic Cache Garbage Collection - React Virtual handles automatic cache purging for inactive queries and garbage collection. This can mean a much smaller memory footprint for apps that consume a lot of data or data that is changing often in a single session
- No Default Data Fetcher Function - React Virtual does not ship with a default fetcher (but can easily be wrapped inside of a custom hook to achieve the same functionality)
useMutation
- A dedicated hook for handling generic lifecycles around triggering mutations and handling their side-effects in applications. SWR does not ship with anything similar, and you may find yourself reimplementing most if not all of useMutation
's functionality in user-land. With this hook, you can extend the lifecycle of your mutations to reliably handle successful refetching strategies, failure rollbacks and error handling.- Prefetching - React Virtual ships with 1st class prefetching utilities which not only come in handy with non-suspenseful apps but also make fetch-as-you-render patterns possible with React Virtual. SWR does not come with similar utilities and relies on
<link rel='preload'>
and/or manually fetching and updating the query cache - Query cancellation integration is baked into React Virtual. You can easily use this to wire up request cancellation in most popular fetching libraries, including but not limited to fetch and axios.
- Query Key Generation - React Virtual uses query key generation, query variables, and implicit query grouping. The query key and variables that are passed to a query are less URL/Query-based by nature and much more flexible. All items supplied to the query key array are used to compute the unique key for a query (using a stable and deterministic sorting/hashing implementation). This means you can spend less time thinking about precise key matching, but more importantly, allows you to use partial query-key matching when refetching, updating, or removing queries in mass eg. you can refetch every query that starts with a
todos
in its key, regardless of variables, or you can target specific queries with (or without) variables, and even use functional filtering to select queries in most places. This architecture is much more robust and forgiving especially for larger apps.
Examples
- Sandbox - CodeSandbox - Source
- Shows examples of Row, Column, and Grid layouts
- Shows examples of fixed, variable, and dynamic sizing
This library is being built and maintained by me, @tannerlinsley and I am always in need of more support to keep projects like this afloat. If you would like to get premium support, add your logo or name on this README, or simply just contribute to my open source Sponsorship goal, visit my Github Sponsors page!
Documentation
Installation
$ npm i --save react-virtual
$ yarn add react-virtual
React Virtual uses Scarf to collect anonymized installation analytics. These analytics help support the maintainers of this library. However, if you'd like to opt out, you can do so by setting scarfSettings.enabled = false
in your project's package.json
. Alternatively, you can set the environment variable SCARF_ANALYTICS=false
before you install.
Sample
This is just a quick sample of what it looks like to use React Virtual. Please refer to the examples for more usage patterns.
function RowVirtualizerFixed() {
const parentRef = React.useRef()
const rowVirtualizer = useVirtual({
size: 10000,
parentRef,
estimateSize: React.useCallback(() => 35, []),
})
return (
<>
<div
ref={parentRef}
className="List"
style={{
height: `150px`,
width: `300px`,
overflow: 'auto',
}}
>
<div
className="ListInner"
style={{
height: `${rowVirtualizer.totalSize}px`,
width: '100%',
position: 'relative',
}}
>
{rowVirtualizer.items.map(virtualRow => (
<div
key={virtualRow.index}
className={virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven'}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`,
}}
>
Row {virtualRow.index}
</div>
))}
</div>
</div>
</>
)
}
API
useVirtual
const {
items: [
{ index, start, size, end, measureRef },
],
totalSize,
} = useVirtual({
size,
parentRef,
estimateSize,
overscan,
horiztonal,
})
Options
size: Integer
- Required
- The size of the virtualizer
parentRef: React.useRef(DOMElement)
- Required
- The parent element whose inner-content is scrollable
estimateSize: Function(index) => Integer
- Required
- Must be memoized using
React.useCallback()
- This function recieves the index of each item and should return either:
- A fixed size
- A variable size per-item
- A best-guess size (when using dynamic measurement rendering)
- When this function's memoization changes, the entire list is recalculated
overscan: Integer
- The amount of items to load both behind and ahead of the current window range
- Defaults to
1
horizontal: Boolean
- When
true
, this virtualizer will use width
and scrollLeft
instead of height
and scrollTop
to determine size and offset of virtualized items.
Returns
items: Array<item>
item: Object
index: Integer
start: Integer
- The starting measurement of the item
- Most commonly used for positioning elements
size: Integer
- The static/variable or, if dynamically rendered, the measured size of the item
end: Integer
- The ending measurement of the item
measureRef: React.useRef | Function(el) => void 0
- The ref/function to place on the rendered element to enable dynamic measurement rendering
totalSize: Integer
- The total size of the entire virtualizer
- When using dynamic measurement refs, this number may change as items are measured after they are rendered.
Contributors ✨
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!