Row Virtualization Feature Guide
MRT v1.4 has major virtualization upgrades after switching to
@tanstack/react-virtual
v3.0!
Virtualization is useful when you have a lot of data you want to display client-side all at once, without having to use pagination. Material React Table makes this as simple as possible, thanks to @tanstack/react-virtual
.
NOTE: You should only enable row virtualization if you have a large number of rows. Depending on the size of the table, if you are rendering less than a couple dozen rows at a time, you will actually just be adding extra overhead to the table renders. Virtualization only becomes necessary when you have over 50 rows or so at the same time with no pagination.
Relevant Props
What is Virtualization?
Virtualization, or virtual scrolling, works by only rendering the rows that are visible on the screen. This is useful for performance and user experience, as we can make it appear that there are hundreds, thousands, or tens of thousands of rows in the table all at once, but in reality, the table will only render the couple dozen rows that are visible on the screen.
For more reading on the concept of virtualization, we recommend this blog post by LogRocket.
Does Your Table Even Need Virtualization?
If your table is already paginated, you probably don't need virtualization. If your table is not rendering more than 100 rows at a time, you might not really need virtualization.
There is a tiny bit of extra overhead that gets added to your table's rendering when virtualization is enabled, so don't just enable it for every table. That being said, if your table does have well over 100 rows that it is trying to render all at once without pagination, performance will be night and day once it is enabled.
If your table is not rendering more than 100 rows at a time, you might not need virtualization enabled!
Enable Row Virtualization
Enabling row virtualization is as simple as setting the enableRowVirtualization
prop to true
. However, you will probably also want to turn off pagination, which you can do by setting enablePagination
to false
.
<MaterialReactTablecolumns={columns}data={data}enablePagination={false}enableRowVirtualization/>
WARNING: Don't enable row virtualization conditionally. It may break React's Rule of Hooks, and/or cause other UI jumpiness.
Row Virtualization Side Effects
When Row Virtualization is enabled, a few other props automatically get set internally.
layoutMode
Prop
In MRT Versions 1.3 and earlier, a CSS table-layout: fixed
style was automatically added to the <table>
element to prevent columns wiggling back and forth during scrolling due to body cells having variating widths.
But now in MRT Versions 1.4 and later, the layoutMode
prop is automatically set to the 'grid'
value, which means that all of the table markup will use CSS Grid and Flexbox instead of the traditional semantic styles that usually come with table tags. This is necessary to make the virtualization work properly with decent performance.
enableStickyHeader
Prop
The enableStickyHeader
prop is automatically set to true
when row virtualization is enabled. This keeps the table header sticky and visible while scrolling.
Customize Virtualizer Props
You can adjust some of the virtualizer props that are used internally. The most useful ones are the overscan
and estimateSize
options. You may want to adjust these values if you have unusual row heights that is causing the default scrolling to act weirdly.
<MaterialReactTablecolumns={columns}data={data}enablePagination={false}enableRowVirtualizationvirtualizerProps={{overscan: 25, //adjust the number or rows that are rendered above and below the visible area of the tableestimateSize: () => 200, //if your rows are about 200px tall, set this to make scrollbar size more accurate}}/>
See the official TanStack Virtualizer Options API Docs for more information.
MRT v1.4 upgraded from
react-virtual
v2 to@tanstack/react-virtual
v3.0, which has some breaking changes and virtualizer option name changes. TypeScript hints should help you with any prop name changes, but you can also view the official TanStack Virtual Docs for guidance.
Access Underlying Virtualizer Instance
In a similar way that you can access the underlying table instance, you can also access the underlying virtualizer instance. This can be useful for accessing methods like the scrollToIndex
method, which can be used to programmatically scroll to a specific row.
const virtualizerInstanceRef = useRef<Virtualizer>(null);useEffect(() => {if (virtualizerInstanceRef.current) {//scroll to the top of the table when sorting changesvirtualizerInstanceRef.current.scrollToIndex(0);}}, [sorting]);return (<MaterialReactTablecolumns={columns}data={data}enableRowVirtualizationvirtualizerInstanceRef={virtualizerInstanceRef}/>);
See the official TanStack Virtualizer Instance API Docs for more information.
Full Row Virtualization Example
Try out the performance of the table below with 10,000 rows! Filtering, Search, and Sorting also maintain usable performance.
# | First Name | Middle Name | Last Name | Email Address | Phone Number | Address | Zip Code | City | State | Country | Pet Name | Age |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1import React, { FC, useEffect, useMemo, useRef, useState } from 'react';2import MaterialReactTable, { MRT_ColumnDef } from 'material-react-table';3import type { SortingState } from '@tanstack/react-table';4import type { Virtualizer } from '@tanstack/react-virtual';5import { makeData, Person } from './makeData';67const Example: FC = () => {8 const columns = useMemo<MRT_ColumnDef<Person>[]>(9 //column definitions...66 );6768 //optionally access the underlying virtualizer instance69 const virtualizerInstanceRef =70 useRef<Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);7172 const [data, setData] = useState<Person[]>([]);73 const [isLoading, setIsLoading] = useState(true);74 const [sorting, setSorting] = useState<SortingState>([]);7576 useEffect(() => {77 if (typeof window !== 'undefined') {78 setData(makeData(10_000));79 setIsLoading(false);80 }81 }, []);8283 useEffect(() => {84 if (virtualizerInstanceRef.current) {85 //scroll to the top of the table when the sorting changes86 virtualizerInstanceRef.current.scrollToIndex(0);87 }88 }, [sorting]);8990 return (91 <MaterialReactTable92 columns={columns}93 data={data} //10,000 rows94 enableBottomToolbar={false}95 enableGlobalFilterModes96 enablePagination={false}97 enableRowNumbers98 enableRowVirtualization99 muiTableContainerProps={{ sx: { maxHeight: '600px' } }}100 onSortingChange={setSorting}101 state={{ isLoading, sorting }}102 virtualizerInstanceRef={virtualizerInstanceRef} //optional103 virtualizerProps={{ overscan: 8 }} //optionally customize the virtualizer104 />105 );106};107108export default Example;109
View Extra Storybook Examples