MRT logoMantine React Table

Virtualized Example

Mantine React Table has a built-in row virtualization feature (via @tanstack/react-virual) that lets you to render a large number of rows without major performance issues.

Try out the performance of the table below with 10,000 rows and over a dozen columns! Filtering, Search, and Sorting also maintain usable performance.

Be sure to also check out the full virtualization feature guide docs to learn about both Row and Column Virtualization.

NOTE: You should only enable row virtualization if you have a large number of rows or columns. Depending on the size of the table, if you are rendering fewer 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 or dozens of columns.

import '@mantine/core/styles.css';
import '@mantine/dates/styles.css'; //if using mantine date picker features
import 'mantine-react-table/styles.css'; //make sure MRT styles were imported in your app root (once)
import { useEffect, useMemo, useRef, useState } from 'react';
import {
  type MRT_ColumnDef,
  type MRT_SortingState,
  type MRT_RowVirtualizer,
} from 'mantine-react-table';
import { makeData, type Person } from './makeData';

const Example = () => {
  const columns = useMemo<MRT_ColumnDef<Person>[]>(
    () => [
        accessorKey: 'firstName',
        header: 'First Name',
        size: 150,
        accessorKey: 'middleName',
        header: 'Middle Name',
        size: 150,
        accessorKey: 'lastName',
        header: 'Last Name',
        size: 150,
        accessorKey: 'email',
        header: 'Email Address',
        size: 300,
        accessorKey: 'phoneNumber',
        header: 'Phone Number',
        size: 250,
        accessorKey: 'address',
        header: 'Address',
        size: 300,
        accessorKey: 'zipCode',
        header: 'Zip Code',
        accessorKey: 'city',
        header: 'City',
        size: 220,
        accessorKey: 'state',
        header: 'State',
        accessorKey: 'country',
        header: 'Country',
        size: 350,
        accessorKey: 'petName',
        header: 'Pet Name',
        accessorKey: 'age',
        header: 'Age',
        accessorKey: 'salary',
        header: 'Salary',
        accessorKey: 'dateOfBirth',
        header: 'Date of Birth',
        accessorKey: 'dateOfJoining',
        header: 'Date of Joining',
        accessorKey: 'isActive',
        header: 'Is Active',

  //optionally access the underlying virtualizer instance
  const rowVirtualizerInstanceRef = useRef<MRT_RowVirtualizer>(null);

  const [data, setData] = useState<Person[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [sorting, setSorting] = useState<MRT_SortingState>([]);

  useEffect(() => {
    if (typeof window !== 'undefined') {
  }, []);

  useEffect(() => {
    try {
      //scroll to the top of the table when the sorting changes
    } catch (e) {
  }, [sorting]);

  const table = useMantineReactTable({
    data, //10,000 rows
    enableBottomToolbar: false,
    enableColumnResizing: true,
    enableColumnVirtualization: true,
    enableGlobalFilterModes: true,
    enablePagination: false,
    enableColumnPinning: true,
    enableRowNumbers: true,
    enableRowVirtualization: true,
    mantineTableContainerProps: { style: { maxHeight: '600px' } },
    onSortingChange: setSorting,
    state: { isLoading, sorting },
    rowVirtualizerInstanceRef, //optional
    rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
    columnVirtualizerOptions: { overscan: 2 }, //optionally customize the column virtualizer

  return <MantineReactTable table={table} />;

export default Example;

View Extra Storybook Examples