MRT logoMaterial React Table

    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

    1
    boolean
    2
    MutableRefObject<Virtualizer | null>
    3
    Partial<VirtualizerOptions<HTMLDivElement>>

    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.

    <MaterialReactTable
    columns={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.

    <MaterialReactTable
    columns={columns}
    data={data}
    enablePagination={false}
    enableRowVirtualization
    virtualizerProps={{
    overscan: 25, //adjust the number or rows that are rendered above and below the visible area of the table
    estimateSize: () => 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 changes
    virtualizerInstanceRef.current.scrollToIndex(0);
    }
    }, [sorting]);
    return (
    <MaterialReactTable
    columns={columns}
    data={data}
    enableRowVirtualization
    virtualizerInstanceRef={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.


    Demo

    Open StackblitzOpen Code SandboxOpen on GitHub

    Source Code

    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';
    6
    7const Example: FC = () => {
    8 const columns = useMemo<MRT_ColumnDef<Person>[]>(
    9 //column definitions...
    66 );
    67
    68 //optionally access the underlying virtualizer instance
    69 const virtualizerInstanceRef =
    70 useRef<Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);
    71
    72 const [data, setData] = useState<Person[]>([]);
    73 const [isLoading, setIsLoading] = useState(true);
    74 const [sorting, setSorting] = useState<SortingState>([]);
    75
    76 useEffect(() => {
    77 if (typeof window !== 'undefined') {
    78 setData(makeData(10_000));
    79 setIsLoading(false);
    80 }
    81 }, []);
    82
    83 useEffect(() => {
    84 if (virtualizerInstanceRef.current) {
    85 //scroll to the top of the table when the sorting changes
    86 virtualizerInstanceRef.current.scrollToIndex(0);
    87 }
    88 }, [sorting]);
    89
    90 return (
    91 <MaterialReactTable
    92 columns={columns}
    93 data={data} //10,000 rows
    94 enableBottomToolbar={false}
    95 enableGlobalFilterModes
    96 enablePagination={false}
    97 enableRowNumbers
    98 enableRowVirtualization
    99 muiTableContainerProps={{ sx: { maxHeight: '600px' } }}
    100 onSortingChange={setSorting}
    101 state={{ isLoading, sorting }}
    102 virtualizerInstanceRef={virtualizerInstanceRef} //optional
    103 virtualizerProps={{ overscan: 8 }} //optionally customize the virtualizer
    104 />
    105 );
    106};
    107
    108export default Example;
    109

    View Extra Storybook Examples