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.
1import { useEffect, useMemo, useRef, useState } from 'react';2import {3MantineReactTable,4useMantineReactTable,5type MRT_ColumnDef,6type MRT_SortingState,7type MRT_Virtualizer,8} from 'mantine-react-table';9import { makeData, type Person } from './makeData';1011const Example = () => {12const columns = useMemo<MRT_ColumnDef<Person>[]>(13() => [14{15accessorKey: 'firstName',16header: 'First Name',17size: 150,18},19{20accessorKey: 'middleName',21header: 'Middle Name',22size: 150,23},24{25accessorKey: 'lastName',26header: 'Last Name',27size: 150,28},29{30accessorKey: 'email',31header: 'Email Address',32size: 300,33},34{35accessorKey: 'phoneNumber',36header: 'Phone Number',37size: 250,38},39{40accessorKey: 'address',41header: 'Address',42size: 300,43},44{45accessorKey: 'zipCode',46header: 'Zip Code',47},48{49accessorKey: 'city',50header: 'City',51size: 220,52},53{54accessorKey: 'state',55header: 'State',56},57{58accessorKey: 'country',59header: 'Country',60size: 350,61},62{63accessorKey: 'petName',64header: 'Pet Name',65},66{67accessorKey: 'age',68header: 'Age',69},70{71accessorKey: 'salary',72header: 'Salary',73},74{75accessorKey: 'dateOfBirth',76header: 'Date of Birth',77},78{79accessorKey: 'dateOfJoining',80header: 'Date of Joining',81},82{83accessorKey: 'isActive',84header: 'Is Active',85},86],87[],88);8990//optionally access the underlying virtualizer instance91const rowVirtualizerInstanceRef =92useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);9394const [data, setData] = useState<Person[]>([]);95const [isLoading, setIsLoading] = useState(true);96const [sorting, setSorting] = useState<MRT_SortingState>([]);9798useEffect(() => {99if (typeof window !== 'undefined') {100setData(makeData(10_000));101setIsLoading(false);102}103}, []);104105useEffect(() => {106try {107//scroll to the top of the table when the sorting changes108rowVirtualizerInstanceRef.current?.scrollToIndex(0);109} catch (e) {110console.log(e);111}112}, [sorting]);113114const table = useMantineReactTable({115columns,116data, //10,000 rows117enableBottomToolbar: false,118enableColumnResizing: true,119enableColumnVirtualization: true,120enableGlobalFilterModes: true,121enablePagination: false,122enablePinning: true,123enableRowNumbers: true,124enableRowVirtualization: true,125mantineTableContainerProps: { sx: { maxHeight: '600px' } },126onSortingChange: setSorting,127state: { isLoading, sorting },128rowVirtualizerInstanceRef, //optional129rowVirtualizerProps: { overscan: 5 }, //optionally customize the row virtualizer130columnVirtualizerProps: { overscan: 2 }, //optionally customize the column virtualizer131});132133return <MantineReactTable table={table} />;134};135136export default Example;
1import { useEffect, useMemo, useRef, useState } from 'react';2import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';3import { makeData } from './makeData';45const Example = () => {6const columns = useMemo(7() => [8{9accessorKey: 'firstName',10header: 'First Name',11size: 150,12},13{14accessorKey: 'middleName',15header: 'Middle Name',16size: 150,17},18{19accessorKey: 'lastName',20header: 'Last Name',21size: 150,22},23{24accessorKey: 'email',25header: 'Email Address',26size: 300,27},28{29accessorKey: 'phoneNumber',30header: 'Phone Number',31size: 250,32},33{34accessorKey: 'address',35header: 'Address',36size: 300,37},38{39accessorKey: 'zipCode',40header: 'Zip Code',41},42{43accessorKey: 'city',44header: 'City',45},46{47accessorKey: 'state',48header: 'State',49},50{51accessorKey: 'country',52header: 'Country',53size: 350,54},55{56accessorKey: 'petName',57header: 'Pet Name',58},59{60accessorKey: 'age',61header: 'Age',62},63{64accessorKey: 'salary',65header: 'Salary',66},67{68accessorKey: 'dateOfBirth',69header: 'Date of Birth',70},71{72accessorKey: 'dateOfJoining',73header: 'Date of Joining',74},75{76accessorKey: 'isActive',77header: 'Is Active',78},79],80[],81);8283//optionally access the underlying virtualizer instance84const rowVirtualizerInstanceRef = useRef(null);8586const [data, setData] = useState([]);87const [isLoading, setIsLoading] = useState(true);88const [sorting, setSorting] = useState([]);8990useEffect(() => {91if (typeof window !== 'undefined') {92setData(makeData(10_000));93setIsLoading(false);94}95}, []);9697useEffect(() => {98try {99//scroll to the top of the table when the sorting changes100rowVirtualizerInstanceRef.current?.scrollToIndex(0);101} catch (e) {102console.log(e);103}104}, [sorting]);105106const table = useMantineReactTable({107columns,108data, //10,000 rows109enableBottomToolbar: false,110enableColumnResizing: true,111enableColumnVirtualization: true,112enableGlobalFilterModes: true,113enablePagination: false,114enablePinning: true,115enableRowNumbers: true,116enableRowVirtualization: true,117mantineTableContainerProps: { sx: { maxHeight: '600px' } },118onSortingChange: setSorting,119state: { isLoading, sorting },120rowVirtualizerInstanceRef, //optional121rowVirtualizerProps: { overscan: 5 }, //optionally customize the row virtualizer122columnVirtualizerProps: { overscan: 2 }, //optionally customize the column virtualizer123});124125return <MantineReactTable table={table} />;126};127128export default Example;
1import { useEffect, useMemo, useRef, useState } from 'react';2import {3MantineReactTable,4type MRT_ColumnDef,5type MRT_SortingState,6type MRT_Virtualizer,7} from 'mantine-react-table';8import { makeData, type Person } from './makeData';910const Example = () => {11const columns = useMemo<MRT_ColumnDef<Person>[]>(12() => [13{14accessorKey: 'firstName',15header: 'First Name',16size: 150,17},18{19accessorKey: 'middleName',20header: 'Middle Name',21size: 150,22},23{24accessorKey: 'lastName',25header: 'Last Name',26size: 150,27},28{29accessorKey: 'email',30header: 'Email Address',31size: 300,32},33{34accessorKey: 'phoneNumber',35header: 'Phone Number',36size: 250,37},38{39accessorKey: 'address',40header: 'Address',41size: 300,42},43{44accessorKey: 'zipCode',45header: 'Zip Code',46},47{48accessorKey: 'city',49header: 'City',50size: 220,51},52{53accessorKey: 'state',54header: 'State',55},56{57accessorKey: 'country',58header: 'Country',59size: 350,60},61{62accessorKey: 'petName',63header: 'Pet Name',64},65{66accessorKey: 'age',67header: 'Age',68},69{70accessorKey: 'salary',71header: 'Salary',72},73{74accessorKey: 'dateOfBirth',75header: 'Date of Birth',76},77{78accessorKey: 'dateOfJoining',79header: 'Date of Joining',80},81{82accessorKey: 'isActive',83header: 'Is Active',84},85],86[],87);8889//optionally access the underlying virtualizer instance90const rowVirtualizerInstanceRef =91useRef<MRT_Virtualizer<HTMLDivElement, HTMLTableRowElement>>(null);9293const [data, setData] = useState<Person[]>([]);94const [isLoading, setIsLoading] = useState(true);95const [sorting, setSorting] = useState<MRT_SortingState>([]);9697useEffect(() => {98if (typeof window !== 'undefined') {99setData(makeData(10_000));100setIsLoading(false);101}102}, []);103104useEffect(() => {105try {106//scroll to the top of the table when the sorting changes107rowVirtualizerInstanceRef.current?.scrollToIndex(0);108} catch (e) {109console.log(e);110}111}, [sorting]);112113return (114<MantineReactTable115columns={columns}116data={data} //10,000 rows117enableBottomToolbar={false}118enableColumnResizing119enableColumnVirtualization120enableGlobalFilterModes121enablePagination={false}122enablePinning123enableRowNumbers124enableRowVirtualization125mantineTableContainerProps={{ sx: { maxHeight: '600px' } }}126onSortingChange={setSorting}127state={{ isLoading, sorting }}128rowVirtualizerInstanceRef={rowVirtualizerInstanceRef} //optional129rowVirtualizerProps={{ overscan: 5 }} //optionally customize the row virtualizer130columnVirtualizerProps={{ overscan: 2 }} //optionally customize the column virtualizer131/>132);133};134135export default Example;
View Extra Storybook Examples