MRT logoMantine React Table

On This Page

    Virtualization Feature Guide

    Virtualization is useful when you have a lot of data you want to display client-side all at once, without having to use pagination. Mantine 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 over 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 over 12 columns, you probably do not need column virtualization.
    There is a tiny bit 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 over 100 rows that it is trying to render all at once without pagination, performance will be night and day once it is 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.
    const table = useMantineReactTable({
    columns,
    data,
    enablePagination: false, //turn off pagination
    enableRowVirtualization: true, //enable row virtualization
    });
    Take a look at the example below with 10,000 rows!
    1
    import { useEffect, useMemo, useRef, useState } from 'react';
    2
    import {
    3
    MantineReactTable,
    4
    useMantineReactTable,
    5
    type MRT_ColumnDef,
    6
    type MRT_SortingState,
    7
    type MRT_Virtualizer,
    8
    } from 'mantine-react-table';
    9
    import { makeData, type Person } from './makeData';
    10
    11
    const Example = () => {
    12
    const columns = useMemo<MRT_ColumnDef<Person>[]>(
    13
    () => [
    14
    {
    15
    accessorKey: 'firstName',
    16
    header: 'First Name',
    17
    size: 150,
    18
    },
    19
    {
    20
    accessorKey: 'middleName',
    21
    header: 'Middle Name',
    22
    size: 150,
    23
    },
    24
    {
    25
    accessorKey: 'lastName',
    26
    header: 'Last Name',
    27
    size: 150,
    28
    },
    29
    {
    30
    accessorKey: 'email',
    31
    header: 'Email Address',
    32
    size: 300,
    33
    },
    34
    {
    35
    accessorKey: 'address',
    36
    header: 'Address',
    37
    },
    38
    {
    39
    accessorKey: 'zipCode',
    40
    header: 'Zip Code',
    41
    },
    42
    {
    43
    accessorKey: 'city',
    44
    header: 'City',
    45
    },
    46
    {
    47
    accessorKey: 'state',
    48
    header: 'State',
    49
    },
    50
    {
    51
    accessorKey: 'country',
    52
    header: 'Country',
    53
    },
    54
    ],
    55
    [],
    56
    );
    57
    58
    //optionally access the underlying virtualizer instance
    59
    const rowVirtualizerInstanceRef =
    60
    useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);
    61
    62
    const [data, setData] = useState<Person[]>([]);
    63
    const [isLoading, setIsLoading] = useState(true);
    64
    const [sorting, setSorting] = useState<MRT_SortingState>([]);
    65
    66
    useEffect(() => {
    67
    if (typeof window !== 'undefined') {
    68
    setData(makeData(10_000));
    69
    setIsLoading(false);
    70
    }
    71
    }, []);
    72
    73
    useEffect(() => {
    74
    try {
    75
    //scroll to the top of the table when the sorting changes
    76
    rowVirtualizerInstanceRef.current?.scrollToIndex(0);
    77
    } catch (e) {
    78
    console.log(e);
    79
    }
    80
    }, [sorting]);
    81
    82
    const table = useMantineReactTable({
    83
    columns,
    84
    data, //10,000 rows
    85
    enableBottomToolbar: false,
    86
    enableGlobalFilterModes: true,
    87
    enablePagination: false,
    88
    enableRowNumbers: true,
    89
    enableRowVirtualization: true,
    90
    mantineTableContainerProps: { sx: { maxHeight: '600px' } },
    91
    onSortingChange: setSorting,
    92
    state: { isLoading, sorting },
    93
    rowVirtualizerProps: { overscan: 8 }, //optionally customize the virtualizer
    94
    });
    95
    96
    return <MantineReactTable table={table} />;
    97
    };
    98
    99
    export default Example;
    1
    import { useEffect, useMemo, useRef, useState } from 'react';
    2
    import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
    3
    import { makeData } from './makeData';
    4
    5
    const Example = () => {
    6
    const columns = useMemo(
    7
    () => [
    8
    {
    9
    accessorKey: 'firstName',
    10
    header: 'First Name',
    11
    size: 150,
    12
    },
    13
    {
    14
    accessorKey: 'middleName',
    15
    header: 'Middle Name',
    16
    size: 150,
    17
    },
    18
    {
    19
    accessorKey: 'lastName',
    20
    header: 'Last Name',
    21
    size: 150,
    22
    },
    23
    {
    24
    accessorKey: 'email',
    25
    header: 'Email Address',
    26
    size: 300,
    27
    },
    28
    {
    29
    accessorKey: 'address',
    30
    header: 'Address',
    31
    },
    32
    {
    33
    accessorKey: 'zipCode',
    34
    header: 'Zip Code',
    35
    },
    36
    {
    37
    accessorKey: 'city',
    38
    header: 'City',
    39
    },
    40
    {
    41
    accessorKey: 'state',
    42
    header: 'State',
    43
    },
    44
    {
    45
    accessorKey: 'country',
    46
    header: 'Country',
    47
    },
    48
    ],
    49
    [],
    50
    );
    51
    52
    //optionally access the underlying virtualizer instance
    53
    const rowVirtualizerInstanceRef = useRef(null);
    54
    55
    const [data, setData] = useState([]);
    56
    const [isLoading, setIsLoading] = useState(true);
    57
    const [sorting, setSorting] = useState([]);
    58
    59
    useEffect(() => {
    60
    if (typeof window !== 'undefined') {
    61
    setData(makeData(10_000));
    62
    setIsLoading(false);
    63
    }
    64
    }, []);
    65
    66
    useEffect(() => {
    67
    try {
    68
    //scroll to the top of the table when the sorting changes
    69
    rowVirtualizerInstanceRef.current?.scrollToIndex(0);
    70
    } catch (e) {
    71
    console.log(e);
    72
    }
    73
    }, [sorting]);
    74
    75
    const table = useMantineReactTable({
    76
    columns,
    77
    data, //10,000 rows
    78
    enableBottomToolbar: false,
    79
    enableGlobalFilterModes: true,
    80
    enablePagination: false,
    81
    enableRowNumbers: true,
    82
    enableRowVirtualization: true,
    83
    mantineTableContainerProps: { sx: { maxHeight: '600px' } },
    84
    onSortingChange: setSorting,
    85
    state: { isLoading, sorting },
    86
    rowVirtualizerProps: { overscan: 8 }, //optionally customize the virtualizer
    87
    });
    88
    89
    return <MantineReactTable table={table} />;
    90
    };
    91
    92
    export default Example;

    Enable Column Virtualization

    Enabling column virtualization is also as simple as setting the enableColumnVirtualization table option to true.
    const table = useMantineReactTable({
    columns,
    data,
    enableColumnVirtualization: true, //enable column virtualization
    });
    Take a look at the example below with 500 columns!
    1-10 of 10
    1
    import { useRef } from 'react';
    2
    import {
    3
    MantineReactTable,
    4
    useMantineReactTable,
    5
    type MRT_Virtualizer,
    6
    } from 'mantine-react-table';
    7
    import { fakeColumns, fakeData } from './makeData';
    8
    9
    const Example = () => {
    10
    //optionally access the underlying virtualizer instance
    11
    const columnVirtualizerInstanceRef =
    12
    useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableCellElement>>(null);
    13
    14
    const table = useMantineReactTable({
    15
    columnVirtualizerInstanceRef, //optional
    16
    columnVirtualizerProps: { overscan: 4 }, //optionally customize the virtualizer
    17
    columns: fakeColumns, //500 columns
    18
    data: fakeData,
    19
    enableColumnVirtualization: true,
    20
    enablePinning: true,
    21
    enableRowNumbers: true,
    22
    });
    23
    24
    return <MantineReactTable table={table} />;
    25
    };
    26
    27
    export default Example;
    1
    import { useRef } from 'react';
    2
    import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
    3
    import { fakeColumns, fakeData } from './makeData';
    4
    5
    const Example = () => {
    6
    //optionally access the underlying virtualizer instance
    7
    const columnVirtualizerInstanceRef = useRef(null);
    8
    9
    const table = useMantineReactTable({
    10
    columnVirtualizerInstanceRef, //optional
    11
    columnVirtualizerProps: { overscan: 4 }, //optionally customize the virtualizer
    12
    columns: fakeColumns, //500 columns
    13
    data: fakeData,
    14
    enableColumnVirtualization: true,
    15
    enablePinning: true,
    16
    enableRowNumbers: true,
    17
    });
    18
    19
    return <MantineReactTable table={table} />;
    20
    };
    21
    22
    export default Example;
    WARNING: Do NOT enable row or column virtualization conditionally. It may 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 table options automatically get set internally.

    layoutMode Table Option

    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 Table Option

    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 rowVirtualizerProps and columnVirtualizerProps table options. 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 act weirdly.
    const table = useMantineReactTable({
    columns,
    data,
    enableColumnVirtualization: true,
    enablePagination: false,
    enableRowVirtualization: true,
    columnVirtualizerProps: {
    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
    },
    rowVirtualizerProps: {
    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.

    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
    try {
    rowVirtualizerInstanceRef.current.scrollToIndex(0);
    } catch (error) {
    console.error(error);
    }
    }
    }, [sorting]);
    const table = useMantineReactTable({
    columns,
    data,
    enableColumnVirtualization: true,
    enablePagination: false,
    enableRowVirtualization: true,
    rowVirtualizerInstanceRef,
    columnVirtualizerInstanceRef,
    });
    return <MantineReactTable table={table} />;
    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
    You can help make these docs better! PRs are Welcome
    Using Material-UI instead of Mantine?
    Check out Material React Table