MRT logoMantine React Table

On This Page

    State Management Guide

    Mantine React Table does not try to hide any of its internal state from you. You can initialize state with custom initial values, manage individual states yourself as you discover the need to have access to them, or store your own reference to the entire table instance wherever you need to.
    This is all optional, of course. If you do not need access to any of the state, you do not need to do anything and it will just automatically be managed internally.
    See the State Options API Docs for more information on which states are available for you to manage.

    Relevant Table Options

    1
    Partial<MRT_TableState<TData>>
    Table State Management Guide
    2
    Partial<MRT_TableState<TData>>
    Table State Management Guide

    Populate Initial State

    If all you care about is setting parts of the initial or default state when the table mounts, then you may be able to specify that state in the initialState prop and not have to worry about managing the state yourself.
    For example, let's say you do not need access to the showColumnFilters state, but you want to set the default value to true when the table mounts. You can do that with the initialState prop:
    const table = useMantineReactTable({
    columns,
    data,
    initialState: {
    density: 'xs', //set default density to compact
    expanded: true, //expand all rows by default
    pagination: { pageIndex: 0, pageSize: 15 }, //set different default page size
    showColumnFilters: true, //show filters by default
    sorting: [{ id: 'name', desc: false }], //sort by name ascending by default
    },
    });
    return <MantineReactTable table={table} />;
    Note: If you use both initialState and state, the state initializer in state prop will take precedence and overwrite the same state values in initialState. So just use either initialState or state, not both for the same states.

    Manage Individual States as Needed

    It is pretty common to need to manage certain state yourself, so that you can react to changes in that state, or have easy access to it when sending it to an API.
    You can pass in any state that you are managing yourself to the state prop, and it will be used instead of the internal state. Each state property option also has a corresponding on[StateName]Change callback that you can use set/update your managed state as it changes internally in the table.
    For example, let's say you need to store the pagination, sorting, and row selection states in a place where you can easily access it in order to use it in parameters for an API call.
    const [pagination, setPagination] = useState({
    pageIndex: 0,
    pageSize: 15, //set different default page size by initializing the state here
    });
    const [rowSelection, setRowSelection] = useState({});
    const [sorting, setSorting] = useState([{ id: 'name', desc: false }]);
    //see example at bottom of page for alternatives to useEffect here
    useEffect(() => {
    //do something when the pagination state changes
    }, [pagination]);
    const table = useMantineReactTable({
    columns,
    data,
    getRowId: (originalRow) => row.username,
    onPaginationChange: setPagination,
    onRowSelectionChange: setRowSelection,
    onSortingChange: setSorting,
    state: { pagination, rowSelection, sorting }, //must pass states back down if using their on[StateName]Change callbacks
    });
    return <MantineReactTable table={table} />;

    Add Side Effects in Set State Callbacks

    In React 18 and beyond, it is becoming more discouraged to use useEffect to react to state changes, because in React Strict Mode (and maybe future versions of React), the useEffect hook may run twice per render. Instead, more event driven functions are recommended to be used. Here is an example for how that looks here. The callback signature for the on[StateName]Change works just like a React setState callback from the useState hook. This means that you have to check if the updater is a function or not, and then call the setState function with the updater callback if it is a function.
    const [pagination, setPagination] = useState({
    pageIndex: 0,
    pageSize: 15,
    });
    const handlePaginationChange = (updater: MRT_Updater<PaginationState>) => {
    //call the setState as normal, but need to check if using an updater callback with a previous state
    setPagination((prevPagination) =>
    //if updater is a function, call it with the previous state, otherwise just use the updater value
    updater instanceof Function ? updater(prevPagination) : updater,
    );
    //put more code for your side effects here, guaranteed to only run once, even in React Strict Mode
    };
    const table = useMantineReactTable({
    columns,
    data,
    onPaginationChange: handlePaginationChange,
    state: { pagination },
    });
    return <MantineReactTable table={table} />;

    Read From the Table Instance

    Note: Previously, in early MRT v1 betas, you could use the tableInstanceRef prop to get access to the table instance. This is no longer necessary as the useMantineReactTable hook now just returns the table instance directly.
    The useMantineReactTable hook returns the table instance. The <MantineReactTable /> needs the table instance for all of its internal logic, but you can also use it for your own purposes.
    const table = useMantineReactTable({
    columns,
    data,
    //...
    });
    const someEventHandler = (event) => {
    console.info(table.getRowModel().rows); //example - get access to all page rows in the table
    console.info(table.getSelectedRowModel()); //example - get access to all selected rows in the table
    console.info(table.getState().sorting); //example - get access to the current sorting state without having to manage it yourself
    };
    return (
    <div>
    <ExternalButton onClick={someEventHandler}>
    Export or Something
    </ExternalButton>
    <MantineReactTable table={table} />
    </div>
    );
    The table instance is the same object that you will also see as a provided parameter in many of the other callback functions throughout Mantine React Table, such as all the render... props or the Cell or Header render overrides in the column definition options.
    const columns = useMemo(
    () => [
    {
    Header: 'Name',
    accessor: 'name',
    Cell: ({ cell, table }) => <span>{cell.getValue()}</span>,
    //The `table` parameter from the Cell option params and the `table` are the same object
    },
    ],
    [],
    );
    const table = useMantineReactTable({
    columns,
    data,
    renderTopToolbarCustomActions: ({ table }) => {
    //The `table` parameter here and the table returned from the hook are the same object
    return <Button>Button</Button>;
    },
    });
    return <MantineReactTable table={table} />;

    Persistent State

    Persistent state is not a built-in feature of Mantine React Table, but it is an easy feature to implement yourself using the above patterns with the state prop and the on[StateName]Change callbacks. Here is an example of how you might implement persistent state using sessionStorage:
    AllisonBrownOmahaNebraska10000
    HarrySmithHickmanNebraska20000
    SallyWilliamsonAllianceNebraska30000
    LebronJamesIndianapolisIndiana40000
    MichaelMcGinnisHarrisonburgVirginia150000
    JosephWilliamsValentineNebraska100000
    NoahBrownToledoOhio50000
    MasonZhangSacramentoCalifornia100000
    VioletDoeSan FranciscoCalifornia100000
    1-9 of 9
    1
    import { useEffect, useRef, useState } from 'react';
    2
    import { Button } from '@mantine/core';
    3
    import {
    4
    MantineReactTable,
    5
    useMantineReactTable,
    6
    type MRT_ColumnDef,
    7
    type MRT_ColumnFiltersState,
    8
    type MRT_DensityState,
    9
    type MRT_SortingState,
    10
    type MRT_VisibilityState,
    11
    } from 'mantine-react-table';
    12
    import { data, type Person } from './makeData';
    13
    14
    const columns: MRT_ColumnDef<Person>[] = [
    15
    {
    16
    accessorKey: 'firstName',
    17
    header: 'First Name',
    18
    },
    19
    {
    20
    accessorKey: 'lastName',
    21
    header: 'Last Name',
    22
    },
    23
    {
    24
    accessorKey: 'city',
    25
    header: 'City',
    26
    },
    27
    {
    28
    accessorKey: 'state',
    29
    header: 'State',
    30
    },
    31
    {
    32
    accessorKey: 'salary',
    33
    header: 'Salary',
    34
    },
    35
    ];
    36
    37
    const Example = () => {
    38
    const isFirstRender = useRef(true);
    39
    40
    const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
    41
    [],
    42
    );
    43
    const [columnVisibility, setColumnVisibility] = useState<MRT_VisibilityState>(
    44
    {},
    45
    );
    46
    const [density, setDensity] = useState<MRT_DensityState>('md');
    47
    const [globalFilter, setGlobalFilter] = useState<string | undefined>(
    48
    undefined,
    49
    );
    50
    const [showGlobalFilter, setShowGlobalFilter] = useState(false);
    51
    const [showColumnFilters, setShowColumnFilters] = useState(false);
    52
    const [sorting, setSorting] = useState<MRT_SortingState>([]);
    53
    54
    //load state from local storage
    55
    useEffect(() => {
    56
    const columnFilters = sessionStorage.getItem('mrt_columnFilters_table_1');
    57
    const columnVisibility = sessionStorage.getItem(
    58
    'mrt_columnVisibility_table_1',
    59
    );
    60
    const density = sessionStorage.getItem('mrt_density_table_1');
    61
    const globalFilter = sessionStorage.getItem('mrt_globalFilter_table_1');
    62
    const showGlobalFilter = sessionStorage.getItem(
    63
    'mrt_showGlobalFilter_table_1',
    64
    );
    65
    const showColumnFilters = sessionStorage.getItem(
    66
    'mrt_showColumnFilters_table_1',
    67
    );
    68
    const sorting = sessionStorage.getItem('mrt_sorting_table_1');
    69
    70
    if (columnFilters) {
    71
    setColumnFilters(JSON.parse(columnFilters));
    72
    }
    73
    if (columnVisibility) {
    74
    setColumnVisibility(JSON.parse(columnVisibility));
    75
    }
    76
    if (density) {
    77
    setDensity(JSON.parse(density));
    78
    }
    79
    if (globalFilter) {
    80
    setGlobalFilter(JSON.parse(globalFilter) || undefined);
    81
    }
    82
    if (showGlobalFilter) {
    83
    setShowGlobalFilter(JSON.parse(showGlobalFilter));
    84
    }
    85
    if (showColumnFilters) {
    86
    setShowColumnFilters(JSON.parse(showColumnFilters));
    87
    }
    88
    if (sorting) {
    89
    setSorting(JSON.parse(sorting));
    90
    }
    91
    isFirstRender.current = false;
    92
    }, []);
    93
    94
    //save states to local storage
    95
    useEffect(() => {
    96
    if (isFirstRender.current) return;
    97
    sessionStorage.setItem(
    98
    'mrt_columnFilters_table_1',
    99
    JSON.stringify(columnFilters),
    100
    );
    101
    }, [columnFilters]);
    102
    103
    useEffect(() => {
    104
    if (isFirstRender.current) return;
    105
    sessionStorage.setItem(
    106
    'mrt_columnVisibility_table_1',
    107
    JSON.stringify(columnVisibility),
    108
    );
    109
    }, [columnVisibility]);
    110
    111
    useEffect(() => {
    112
    if (isFirstRender.current) return;
    113
    sessionStorage.setItem('mrt_density_table_1', JSON.stringify(density));
    114
    }, [density]);
    115
    116
    useEffect(() => {
    117
    if (isFirstRender.current) return;
    118
    sessionStorage.setItem(
    119
    'mrt_globalFilter_table_1',
    120
    JSON.stringify(globalFilter ?? ''),
    121
    );
    122
    }, [globalFilter]);
    123
    124
    useEffect(() => {
    125
    if (isFirstRender.current) return;
    126
    sessionStorage.setItem(
    127
    'mrt_showGlobalFilter_table_1',
    128
    JSON.stringify(showGlobalFilter),
    129
    );
    130
    }, [showGlobalFilter]);
    131
    132
    useEffect(() => {
    133
    if (isFirstRender.current) return;
    134
    sessionStorage.setItem(
    135
    'mrt_showColumnFilters_table_1',
    136
    JSON.stringify(showColumnFilters),
    137
    );
    138
    }, [showColumnFilters]);
    139
    140
    useEffect(() => {
    141
    if (isFirstRender.current) return;
    142
    sessionStorage.setItem('mrt_sorting_table_1', JSON.stringify(sorting));
    143
    }, [sorting]);
    144
    145
    const resetState = () => {
    146
    sessionStorage.removeItem('mrt_columnFilters_table_1');
    147
    sessionStorage.removeItem('mrt_columnVisibility_table_1');
    148
    sessionStorage.removeItem('mrt_density_table_1');
    149
    sessionStorage.removeItem('mrt_globalFilter_table_1');
    150
    sessionStorage.removeItem('mrt_showGlobalFilter_table_1');
    151
    sessionStorage.removeItem('mrt_showColumnFilters_table_1');
    152
    sessionStorage.removeItem('mrt_sorting_table_1');
    153
    window.location.reload();
    154
    };
    155
    156
    const table = useMantineReactTable({
    157
    columns,
    158
    data,
    159
    onColumnFiltersChange: setColumnFilters,
    160
    onColumnVisibilityChange: setColumnVisibility,
    161
    onDensityChange: setDensity,
    162
    onGlobalFilterChange: setGlobalFilter,
    163
    onShowColumnFiltersChange: setShowColumnFilters,
    164
    onShowGlobalFilterChange: setShowGlobalFilter,
    165
    onSortingChange: setSorting,
    166
    state: {
    167
    columnFilters,
    168
    columnVisibility,
    169
    density,
    170
    globalFilter,
    171
    showColumnFilters,
    172
    showGlobalFilter,
    173
    sorting,
    174
    },
    175
    renderTopToolbarCustomActions: () => (
    176
    <Button onClick={resetState}>Reset State</Button>
    177
    ),
    178
    });
    179
    180
    return <MantineReactTable table={table} />;
    181
    };
    182
    183
    export default Example;
    1
    import { useEffect, useRef, useState } from 'react';
    2
    import { Button } from '@mantine/core';
    3
    import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';
    4
    import { data } from './makeData';
    5
    6
    const columns = [
    7
    {
    8
    accessorKey: 'firstName',
    9
    header: 'First Name',
    10
    },
    11
    {
    12
    accessorKey: 'lastName',
    13
    header: 'Last Name',
    14
    },
    15
    {
    16
    accessorKey: 'city',
    17
    header: 'City',
    18
    },
    19
    {
    20
    accessorKey: 'state',
    21
    header: 'State',
    22
    },
    23
    {
    24
    accessorKey: 'salary',
    25
    header: 'Salary',
    26
    },
    27
    ];
    28
    29
    const Example = () => {
    30
    const isFirstRender = useRef(true);
    31
    32
    const [columnFilters, setColumnFilters] = useState([]);
    33
    const [columnVisibility, setColumnVisibility] = useState({});
    34
    const [density, setDensity] = useState('md');
    35
    const [globalFilter, setGlobalFilter] = useState(undefined);
    36
    const [showGlobalFilter, setShowGlobalFilter] = useState(false);
    37
    const [showColumnFilters, setShowColumnFilters] = useState(false);
    38
    const [sorting, setSorting] = useState([]);
    39
    40
    //load state from local storage
    41
    useEffect(() => {
    42
    const columnFilters = sessionStorage.getItem('mrt_columnFilters_table_1');
    43
    const columnVisibility = sessionStorage.getItem(
    44
    'mrt_columnVisibility_table_1',
    45
    );
    46
    const density = sessionStorage.getItem('mrt_density_table_1');
    47
    const globalFilter = sessionStorage.getItem('mrt_globalFilter_table_1');
    48
    const showGlobalFilter = sessionStorage.getItem(
    49
    'mrt_showGlobalFilter_table_1',
    50
    );
    51
    const showColumnFilters = sessionStorage.getItem(
    52
    'mrt_showColumnFilters_table_1',
    53
    );
    54
    const sorting = sessionStorage.getItem('mrt_sorting_table_1');
    55
    56
    if (columnFilters) {
    57
    setColumnFilters(JSON.parse(columnFilters));
    58
    }
    59
    if (columnVisibility) {
    60
    setColumnVisibility(JSON.parse(columnVisibility));
    61
    }
    62
    if (density) {
    63
    setDensity(JSON.parse(density));
    64
    }
    65
    if (globalFilter) {
    66
    setGlobalFilter(JSON.parse(globalFilter) || undefined);
    67
    }
    68
    if (showGlobalFilter) {
    69
    setShowGlobalFilter(JSON.parse(showGlobalFilter));
    70
    }
    71
    if (showColumnFilters) {
    72
    setShowColumnFilters(JSON.parse(showColumnFilters));
    73
    }
    74
    if (sorting) {
    75
    setSorting(JSON.parse(sorting));
    76
    }
    77
    isFirstRender.current = false;
    78
    }, []);
    79
    80
    //save states to local storage
    81
    useEffect(() => {
    82
    if (isFirstRender.current) return;
    83
    sessionStorage.setItem(
    84
    'mrt_columnFilters_table_1',
    85
    JSON.stringify(columnFilters),
    86
    );
    87
    }, [columnFilters]);
    88
    89
    useEffect(() => {
    90
    if (isFirstRender.current) return;
    91
    sessionStorage.setItem(
    92
    'mrt_columnVisibility_table_1',
    93
    JSON.stringify(columnVisibility),
    94
    );
    95
    }, [columnVisibility]);
    96
    97
    useEffect(() => {
    98
    if (isFirstRender.current) return;
    99
    sessionStorage.setItem('mrt_density_table_1', JSON.stringify(density));
    100
    }, [density]);
    101
    102
    useEffect(() => {
    103
    if (isFirstRender.current) return;
    104
    sessionStorage.setItem(
    105
    'mrt_globalFilter_table_1',
    106
    JSON.stringify(globalFilter ?? ''),
    107
    );
    108
    }, [globalFilter]);
    109
    110
    useEffect(() => {
    111
    if (isFirstRender.current) return;
    112
    sessionStorage.setItem(
    113
    'mrt_showGlobalFilter_table_1',
    114
    JSON.stringify(showGlobalFilter),
    115
    );
    116
    }, [showGlobalFilter]);
    117
    118
    useEffect(() => {
    119
    if (isFirstRender.current) return;
    120
    sessionStorage.setItem(
    121
    'mrt_showColumnFilters_table_1',
    122
    JSON.stringify(showColumnFilters),
    123
    );
    124
    }, [showColumnFilters]);
    125
    126
    useEffect(() => {
    127
    if (isFirstRender.current) return;
    128
    sessionStorage.setItem('mrt_sorting_table_1', JSON.stringify(sorting));
    129
    }, [sorting]);
    130
    131
    const resetState = () => {
    132
    sessionStorage.removeItem('mrt_columnFilters_table_1');
    133
    sessionStorage.removeItem('mrt_columnVisibility_table_1');
    134
    sessionStorage.removeItem('mrt_density_table_1');
    135
    sessionStorage.removeItem('mrt_globalFilter_table_1');
    136
    sessionStorage.removeItem('mrt_showGlobalFilter_table_1');
    137
    sessionStorage.removeItem('mrt_showColumnFilters_table_1');
    138
    sessionStorage.removeItem('mrt_sorting_table_1');
    139
    window.location.reload();
    140
    };
    141
    142
    const table = useMantineReactTable({
    143
    columns,
    144
    data,
    145
    onColumnFiltersChange: setColumnFilters,
    146
    onColumnVisibilityChange: setColumnVisibility,
    147
    onDensityChange: setDensity,
    148
    onGlobalFilterChange: setGlobalFilter,
    149
    onShowColumnFiltersChange: setShowColumnFilters,
    150
    onShowGlobalFilterChange: setShowGlobalFilter,
    151
    onSortingChange: setSorting,
    152
    state: {
    153
    columnFilters,
    154
    columnVisibility,
    155
    density,
    156
    globalFilter,
    157
    showColumnFilters,
    158
    showGlobalFilter,
    159
    sorting,
    160
    },
    161
    renderTopToolbarCustomActions: () => (
    162
    <Button onClick={resetState}>Reset State</Button>
    163
    ),
    164
    });
    165
    166
    return <MantineReactTable table={table} />;
    167
    };
    168
    169
    export default Example;
    You can help make these docs better! PRs are Welcome
    Using Material-UI instead of Mantine?
    Check out Material React Table