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