MRT logoMaterial React Table

On This Page

    Async Loading UI Feature Guide

    While you are fetching your data, you may want to show some loading indicators. Material React Table has some nice loading UI features built in that look better than a simple spinner.

    This guide is mostly focused on the loading UI features. Make sure to also check out the Remote Data and React Query examples for server-side logic examples.

    Relevant Table Options

    1
    CircularProgressProps | ({ table }) => CircularProgressProps
    Material UI CircularProgress Props
    2
    LinearProgressProps | ({ isTopToolbar, table }) => LinearProgressProps
    Material UI LinearProgress Props
    3
    SkeletonProps | ({ cell, column, row, table }) => SkeletonProps
    Material UI Skeleton Props

    Relevant State Options

    1
    boolean
    false
    2
    boolean
    false
    3
    boolean
    false
    4
    boolean
    false
    5
    boolean
    false

    isLoading UI

    There are three different loading UI features that are built into Material React Table:

    1. Loading Overlay - shows spinner overlay over the table container. (New in V2)

    2. Cell Skeletons - show pretty and shimmering skeletons for each cell.

    3. Linear Progress Bars - shows progress bars above and/or below the table.

    You can use any combination of these loading UIs by managing the showLoadingOverlay, showSkeletons, and showProgressBars states.

    There are also two other loading states that are shortcuts for combining some of the above states:

    • isLoading - shows loading overlay and cell skeletons.

    • isSaving - shows the progress bars and adds spinners to the save buttons in editing features.

    Here is some of the recommended loading UI that you might use with React Query:

    const {
    data = [],
    isLoading: isLoadingTodos,
    isRefetching: isRefetchingTodos,
    } = useQuery({
    /**/
    });
    const { mutate, isPending: isSavingTodos } = useMutation({
    /**/
    });
    const table = useMaterialReactTable({
    columns,
    data,
    state: {
    isLoading: isLoadingTodos, //cell skeletons and loading overlay
    showProgressBars: isRefetchingTodos, //progress bars while refetching
    isSaving: isSavingTodos, //progress bars and save button spinners
    },
    });
    return <MaterialReactTable table={table} />;

    Note: The Loading Overlay UI makes the table container non-interactive while it is showing. This is usually desired while no data is yet in the table. Consider avoiding using the Loading Overlay UI during "refetching" operations like filtering, sorting, or pagination.

    Customize Loading UI

    You can customize the loading UI by passing props to the muiSkeletonProps, muiLinearProgressProps, and muiCircularProgressProps props.

    const table = useMaterialReactTable({
    columns,
    data,
    muiSkeletonProps: {
    animation: 'wave',
    },
    muiLinearProgressProps: {
    color: 'secondary',
    },
    muiCircularProgressProps: {
    color: 'secondary',
    },
    });
    return <MaterialReactTable table={table} />;

    Demo

    Open StackblitzOpen Code SandboxOpen on GitHub
    1-10 of 10

    Source Code

    1import { useMemo } from 'react';
    2import { MaterialReactTable, type MRT_ColumnDef } from 'material-react-table';
    3import { type Person } from './makeData';
    4
    5const data: Array<Person> = [];
    6
    7const Example = () => {
    8 const columns = useMemo<MRT_ColumnDef<Person>[]>(
    9 //column definitions...
    30 );
    31
    32 return (
    33 <MaterialReactTable
    34 columns={columns}
    35 data={data}
    36 state={{ isLoading: true }}
    37 muiCircularProgressProps={{
    38 color: 'secondary',
    39 thickness: 5,
    40 size: 55,
    41 }}
    42 muiSkeletonProps={{
    43 animation: 'pulse',
    44 height: 28,
    45 }}
    46 />
    47 );
    48};
    49
    50export default Example;
    51

    Only Show Progress Bars or Skeletons

    If you do not want both progress bars and cell skeletons to show, you can use the showProgressBars and showSkeletons states, instead.

    const table = useMaterialReactTable({
    columns,
    data,
    state: {
    showProgressBars: true, //or showSkeletons
    },
    });

    Demo

    DylanMurraydmurray@yopmail.comEast Daphne
    RaquelKohlerrkholer33@yopmail.comColumbus
    ErvinReingerereinger@mailinator.comSouth Linda
    BrittanyMcCulloughbmccullough44@mailinator.comLincoln
    BransonFramibframi@yopmain.comNew York
    KevinKleinkklien@mailinator.comNebraska
    1-6 of 6

    Source Code

    1import { useEffect, useMemo, useState } from 'react';
    2import { MaterialReactTable, type MRT_ColumnDef } from 'material-react-table';
    3import { data, type Person } from './makeData';
    4import { Button } from '@mui/material';
    5
    6const Example = () => {
    7 const columns = useMemo<MRT_ColumnDef<Person>[]>(
    8 //column definitions...
    29 );
    30
    31 const [progress, setProgress] = useState(0);
    32
    33 //simulate random progress for demo purposes
    34 useEffect(() => {
    35 const interval = setInterval(() => {
    36 setProgress((oldProgress) => {
    37 const newProgress = Math.random() * 20;
    38 return Math.min(oldProgress + newProgress, 100);
    39 });
    40 }, 1000);
    41 return () => clearInterval(interval);
    42 }, []);
    43
    44 return (
    45 <MaterialReactTable
    46 columns={columns}
    47 data={data}
    48 muiLinearProgressProps={({ isTopToolbar }) => ({
    49 color: 'secondary',
    50 variant: 'determinate', //if you want to show exact progress value
    51 value: progress, //value between 0 and 100
    52 sx: {
    53 display: isTopToolbar ? 'block' : 'none', //hide bottom progress bar
    54 },
    55 })}
    56 renderTopToolbarCustomActions={() => (
    57 <Button onClick={() => setProgress(0)} variant="contained">
    58 Reset
    59 </Button>
    60 )}
    61 state={{ showProgressBars: true }}
    62 />
    63 );
    64};
    65
    66export default Example;
    67

    Full Loading and Server-Side Logic Example

    Here is a copy of the full React Query example.

    Demo

    0-0 of 0

    Source Code

    1import { useMemo, useState } from 'react';
    2import {
    3 MaterialReactTable,
    4 type MRT_ColumnDef,
    5 type MRT_ColumnFiltersState,
    6 type MRT_PaginationState,
    7 type MRT_SortingState,
    8} from 'material-react-table';
    9import { IconButton, Tooltip } from '@mui/material';
    10import RefreshIcon from '@mui/icons-material/Refresh';
    11import {
    12 QueryClient,
    13 QueryClientProvider,
    14 keepPreviousData,
    15 useQuery,
    16} from '@tanstack/react-query';
    17
    18type UserApiResponse = {
    19 data: Array<User>;
    20 meta: {
    21 totalRowCount: number;
    22 };
    23};
    24
    25type User = {
    26 firstName: string;
    27 lastName: string;
    28 address: string;
    29 state: string;
    30 phoneNumber: string;
    31};
    32
    33const Example = () => {
    34 const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(
    35 [],
    36 );
    37 const [globalFilter, setGlobalFilter] = useState('');
    38 const [sorting, setSorting] = useState<MRT_SortingState>([]);
    39 const [pagination, setPagination] = useState<MRT_PaginationState>({
    40 pageIndex: 0,
    41 pageSize: 10,
    42 });
    43
    44 const { data, isError, isRefetching, isLoading, refetch } =
    45 useQuery<UserApiResponse>({
    46 queryKey: [
    47 'table-data',
    48 columnFilters, //refetch when columnFilters changes
    49 globalFilter, //refetch when globalFilter changes
    50 pagination.pageIndex, //refetch when pagination.pageIndex changes
    51 pagination.pageSize, //refetch when pagination.pageSize changes
    52 sorting, //refetch when sorting changes
    53 ],
    54 queryFn: async () => {
    55 const fetchURL = new URL(
    56 '/api/data',
    57 process.env.NODE_ENV === 'production'
    58 ? 'https://www.material-react-table.com'
    59 : 'http://localhost:3000',
    60 );
    61 fetchURL.searchParams.set(
    62 'start',
    63 `${pagination.pageIndex * pagination.pageSize}`,
    64 );
    65 fetchURL.searchParams.set('size', `${pagination.pageSize}`);
    66 fetchURL.searchParams.set(
    67 'filters',
    68 JSON.stringify(columnFilters ?? []),
    69 );
    70 fetchURL.searchParams.set('globalFilter', globalFilter ?? '');
    71 fetchURL.searchParams.set('sorting', JSON.stringify(sorting ?? []));
    72
    73 const response = await fetch(fetchURL.href);
    74 const json = (await response.json()) as UserApiResponse;
    75 return json;
    76 },
    77 placeholderData: keepPreviousData,
    78 });
    79
    80 const columns = useMemo<MRT_ColumnDef<User>[]>(
    81 () => [
    82 {
    83 accessorKey: 'firstName',
    84 header: 'First Name',
    85 },
    86 {
    87 accessorKey: 'lastName',
    88 header: 'Last Name',
    89 },
    90 {
    91 accessorKey: 'address',
    92 header: 'Address',
    93 },
    94 {
    95 accessorKey: 'state',
    96 header: 'State',
    97 },
    98 {
    99 accessorKey: 'phoneNumber',
    100 header: 'Phone Number',
    101 },
    102 ],
    103 [],
    104 );
    105
    106 return (
    107 <MaterialReactTable
    108 columns={columns}
    109 data={data?.data ?? []} //data is undefined on first render
    110 initialState={{ showColumnFilters: true }}
    111 manualFiltering
    112 manualPagination
    113 manualSorting
    114 muiToolbarAlertBannerProps={
    115 isError
    116 ? {
    117 color: 'error',
    118 children: 'Error loading data',
    119 }
    120 : undefined
    121 }
    122 onColumnFiltersChange={setColumnFilters}
    123 onGlobalFilterChange={setGlobalFilter}
    124 onPaginationChange={setPagination}
    125 onSortingChange={setSorting}
    126 renderTopToolbarCustomActions={() => (
    127 <Tooltip arrow title="Refresh Data">
    128 <IconButton onClick={() => refetch()}>
    129 <RefreshIcon />
    130 </IconButton>
    131 </Tooltip>
    132 )}
    133 rowCount={data?.meta?.totalRowCount ?? 0}
    134 state={{
    135 columnFilters,
    136 globalFilter,
    137 isLoading,
    138 pagination,
    139 showAlertBanner: isError,
    140 showProgressBars: isRefetching,
    141 sorting,
    142 }}
    143 />
    144 );
    145};
    146
    147const queryClient = new QueryClient();
    148
    149const ExampleWithReactQueryProvider = () => (
    150 //App.tsx or AppProviders file
    151 <QueryClientProvider client={queryClient}>
    152 <Example />
    153 </QueryClientProvider>
    154);
    155
    156export default ExampleWithReactQueryProvider;
    157