MRT logoMaterial React Table

Virtualization Feature Guide

MRT v1.4 and v1.5 have 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 with both row virtualization and column virtualization support.

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 more than 50 rows or so at the same time with no pagination.

Relevant Table Options

1
MutableRefObject<Virtualizer | null>
2
Partial<VirtualizerOptions<HTMLDivElement, HTMLTableCellElement>>
3
boolean
MRT Virtualization Docs
4
boolean
MRT Virtualization Docs
5
MutableRefObject<Virtualizer | null>
6
Partial<VirtualizerOptions<HTMLDivElement, HTMLTableRowElement>>

What is Virtualization?

Virtualization, or virtual scrolling, works by only rendering the rows or columns 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 even 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, or the handful of columns 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 paginated or you are not going to render more than 50 rows at once, you probably do not need row virtualization.

If your table does not have more than 12 columns, you probably do not need column virtualization.

There is a slight amount of extra overhead that gets added to your table's rendering when virtualization is enabled, so do not just enable it for every table. That being said, if your table does have well more than 100 rows that it is trying to render all at once without pagination, performance should improve significantly once it has been enabled.

Enable Row Virtualization

Enabling row virtualization is as simple as setting the enableRowVirtualization table option to true. However, you will probably also want to turn off pagination, which you can do by setting enablePagination to false.

<MaterialReactTable
columns={columns}
data={data}
enablePagination={false}
enableRowVirtualization
/>

Take a look at the example below with 10,000 rows!

1EphraimGianniOrnJerry47@yahoo.com4081 McKenzie Land36948-0303Fort JoshuaburySouth CarolinaMicronesia
2MabelStephanieCarrollRossie47@hotmail.com825 Denesik Crest25737-5104AntonechesterFloridaFalkland Islands (Malvinas)
3AlyssonMarlinLebsackNorene60@yahoo.com733 Wunsch Glen37708Fort AllenechesterTennesseeHungary
4EmelieAdelineHaagAdelle13@yahoo.com5664 2nd Street94269West ClydesteadArizonaTurks and Caicos Islands
5TinaJovanyLindNed.Orn@yahoo.com29019 Deion Light90558RodrickbergNew JerseyGuatemala
6JeffereySydnieLeuschkeEmerson60@yahoo.com8166 Silver Street51461BrownburyIndianaChristmas Island
7VanessaKennediGottliebSadye.Deckow72@gmail.com4918 Sipes Mall20238LinnealandAlabamaTanzania
8AlekMaiyaKossMaritza_Doyle@hotmail.com9196 S Jackson Street68804-7067Port TyriqueboroughHawaiiIreland
9PearlIvaStrosinVaughn_Mohr@hotmail.com6695 Norberto Oval74932LilahavenNew HampshireMontenegro
10WatsonLarryBradtkeLauriane.Bartell38@hotmail.com554 W 7th Street75374GreenholtcesterVermontGuyana
11EleanoreMarvinWisokyEdison.Rogahn@hotmail.com27749 W 8th Street61857-3508AgustinamouthArkansasBangladesh
12HalleWestonBoehmDarwin_OKon@gmail.com228 Cayla Glen43790ArnoldotownOklahomaMacao
13KristofferBrianaEmardBrian_Lang41@yahoo.com93869 Maynard Forest16995New AldenfurtOklahomaGreece
14NorbertCarmelaAdamsLorenza_Koelpin95@hotmail.com90241 S College Street87147-4499East BuddytonMarylandBahamas
15FatimaKelleyWisokyGranville11@hotmail.com51243 Florencio Tunnel74659RubyfurtLouisianaTanzania
16RigobertoJuddLemkeLonnie_Heaney9@gmail.com55132 N Main Avenue49561-4337West EltonWest VirginiaMauritania

Source Code

1import { useEffect, useMemo, useRef, useState } from 'react';
2import {
3 MaterialReactTable,
4 useMaterialReactTable,
5 type MRT_ColumnDef,
6 type MRT_SortingState,
7 type MRT_Virtualizer,
8} from 'material-react-table';
9import { makeData, type Person } from './makeData';
10
11const Example = () => {
12 const columns = useMemo<MRT_ColumnDef<Person>[]>(
13 //column definitions...
58 );
59
60 //optionally access the underlying virtualizer instance
61 const rowVirtualizerInstanceRef =
62 useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);
63
64 const [data, setData] = useState<Person[]>([]);
65 const [isLoading, setIsLoading] = useState(true);
66 const [sorting, setSorting] = useState<MRT_SortingState>([]);
67
68 useEffect(() => {
69 if (typeof window !== 'undefined') {
70 setData(makeData(10_000));
71 setIsLoading(false);
72 }
73 }, []);
74
75 useEffect(() => {
76 //scroll to the top of the table when the sorting changes
77 try {
78 rowVirtualizerInstanceRef.current?.scrollToIndex?.(0);
79 } catch (error) {
80 console.error(error);
81 }
82 }, [sorting]);
83
84 const table = useMaterialReactTable({
85 columns,
86 data, //10,000 rows
87 enableBottomToolbar: false,
88 enableGlobalFilterModes: true,
89 enablePagination: false,
90 enableRowNumbers: true,
91 enableRowVirtualization: true,
92 muiTableContainerProps: { sx: { maxHeight: '600px' } },
93 onSortingChange: setSorting,
94 state: { isLoading, sorting },
95 rowVirtualizerInstanceRef, //optional
96 rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
97 });
98
99 return <MaterialReactTable table={table} />;
100};
101
102export default Example;
103

Enable Column Virtualization

Enabling column virtualization is also as simple as setting the enableColumnVirtualization table option to true.

<MaterialReactTable columns={columns} data={data} enableColumnVirtualization />

Take a look at the example below with 500 columns!

1AshtynEldridgeEvansSheridanLauryAnaMonteMarshallMarcosJerrell
2WestonJailynEmmetShemarVickyBirdieElectaNovellaBradlyFanny
3EmanuelClevelandEvaJaleelAlannaKennaIgnacioRudolphTheresaNikki
4ThoraJamieCandaceQueenieDwightKeanuEulahVellaBrianneJacques
5DarronMarianaCortneyShannyJimmyFurmanEveretteErynMargretZane
6EmmaleeLucienneRosettaDonnaAlfonsoCarloCheyenneEladioEddieNyah
7IvyJewellAnthonyChanelleKatlynErynColemanWayneLexusSaige
8SophieJeffereyDelilahJulianneEveEstrellaKendallMaxineHobartMohammed
9EmoryBrendanRevaAmandaBrantTheresiaEarnestineGiaMargaretClarabelle
10BeaulahRemingtonGunnerPriceJudeBroderickOrlandoQueenLonzoLouisa
1-10 of 10

Source Code

1import { useRef } from 'react';
2import {
3 MaterialReactTable,
4 useMaterialReactTable,
5 type MRT_Virtualizer,
6} from 'material-react-table';
7import { fakeColumns, fakeData } from './makeData';
8
9const Example = () => {
10 //optionally access the underlying virtualizer instance
11 const columnVirtualizerInstanceRef =
12 useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableCellElement>>(null);
13
14 const table = useMaterialReactTable({
15 columnVirtualizerInstanceRef, //optional
16 columnVirtualizerOptions: { overscan: 4 }, //optionally customize the virtualizer
17 columns: fakeColumns, //500 columns
18 data: fakeData,
19 enableColumnVirtualization: true,
20 enableColumnPinning: true,
21 enableRowNumbers: true,
22 });
23
24 return <MaterialReactTable table={table} />;
25};
26
27export default Example;
28

WARNING: Do not enable row or column virtualization conditionally. This could break React's Rule of Hooks and/or cause other UI jumpiness.

Virtualization Side Effects

When either row or column 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 from wiggling back and forth during scrolling due to body cells having varying widths.

But now in MRT Versions 1.4 and later, the layoutMode table option is automatically set to the 'grid' value when either row or column virtualization is enabled, 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 table option is automatically set to true when row virtualization is enabled. This keeps the table header sticky and visible while scrolling and adds a default max-height of 100vh to the table container.

Customize Virtualizer Props

You can adjust some of the virtualizer props that are used internally with the rowVirtualizerOptions and columnVirtualizerOptions props. The most useful virtualizer props are the overscan and estimateSize options. You may want to adjust these values if you have unusual row heights or column widths that are causing the default scrolling to behave strangely.

<MaterialReactTable
columns={columns}
data={data}
enableColumnVirtualization
enablePagination={false}
enableRowVirtualization
columnVirtualizerOptions={{
overscan: 5, //adjust the number of columns that are rendered to the left and right of the visible area of the table
estimateSize: () => 400, //if your columns are wider or , try tweaking this value to make scrollbar size more accurate
}}
rowVirtualizerOptions={{
overscan: 10, //adjust the number or rows that are rendered above and below the visible area of the table
estimateSize: () => 100, //if your rows are taller than normal, try tweaking this value 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 Instances

In a similar way that you can access the underlying table instance, you can also access the underlying virtualizer instances. This can be useful for accessing methods like the scrollToIndex method, which can be used to programmatically scroll to a specific row or column.

const columnVirtualizerInstanceRef = useRef<Virtualizer>(null);
const rowVirtualizerInstanceRef = useRef<Virtualizer>(null);
useEffect(() => {
if (rowVirtualizerInstanceRef.current) {
//scroll to the top of the table when sorting changes
rowVirtualizerInstanceRef.current.scrollToIndex(0);
}
}, [sorting]);
return (
<MaterialReactTable
columns={columns}
data={data}
enableColumnVirtualization
enableRowVirtualization
rowVirtualizerInstanceRef={rowVirtualizerInstanceRef}
columnVirtualizerInstanceRef={columnVirtualizerInstanceRef}
/>
);

See the official TanStack Virtualizer Instance API Docs for more information.

Full Row and Column Virtualization Example

Try out the performance of the fully virtualized example with 10,000 rows and over a dozen columns! Filtering, search, and sorting also maintain usable performance.

View Extra Storybook Examples

Headless Virtualization with MRT Hooks

If you are building a custom table in headless mode, you can still take advantage of some of the built-in virtualization functionality via the useMRT_ColumnVirtualizer and useMRT_RowVirtualizer hooks, which are wrappers for the TanStack Virtual useVirtualizer hook.

If you go this route, you will need to consult the TanStack Virtual Docs for more information on the exact styles and markup needed for the virtualization to work properly.

Here is the basics of how to use the MRT virtualization hooks:

import {
useMaterialReactTable,
useMRT_Rows,
useMRT_RowVirtualizer,
useMRT_ColumnVirtualizer,
} from 'material-react-table';
const table = useMaterialReactTable({
columns,
data,
enableColumnVirtualization: true,
enableRowVirtualization: true,
columnVirtualizerOptions: {
//...virtualizer options
},
rowVirtualizerOptions: {
//...virtualizer options
},
});
const rows = useMRT_Rows(table); //alternative to table.getRowModel()
const columnVirtualizer = useMRT_ColumnVirtualizer(table);
const rowVirtualizer = useMRT_RowVirtualizer(table);
//down in your row render
const virtualRows = rowVirtualizer.getVirtualItems();
virtualRows.map((virtualRow) => {
const row = rows[virtualRow.index];
return <Row key={row.id} />;
});

Alternatively, just use the useVirtualizer hook directly from @tanstack/react-virtual if you want to build your own virtualization logic from scratch. The MRT hooks are provided as opinionated wrappers for your potential convenience.