Remote Data Fetching Example
You will most likely be using a remote data source for your table, which is fully supported. Here is an example of data being fetched from a remote server but also filtered, paginated, and sorted on the server.
Also, be sure to check out the TanStack React Query Example, which is very similar to this one, except it uses react-query to simplify much of the state management needed for fetching data.
First Name | Last Name | Address | State | Phone Number | |
---|---|---|---|---|---|
No records to display |
0-0 of 0
1import { useEffect, useMemo, useState } from 'react';2import {3MantineReactTable,4useMantineReactTable,5type MRT_ColumnDef,6type MRT_ColumnFiltersState,7type MRT_PaginationState,8type MRT_SortingState,9} from 'mantine-react-table';1011type UserApiResponse = {12data: Array<User>;13meta: {14totalRowCount: number;15};16};1718type User = {19firstName: string;20lastName: string;21address: string;22state: string;23phoneNumber: string;24};2526const Example = () => {27//data and fetching state28const [data, setData] = useState<User[]>([]);29const [isError, setIsError] = useState(false);30const [isLoading, setIsLoading] = useState(false);31const [isRefetching, setIsRefetching] = useState(false);32const [rowCount, setRowCount] = useState(0);3334//table state35const [columnFilters, setColumnFilters] = useState<MRT_ColumnFiltersState>(36[],37);38const [globalFilter, setGlobalFilter] = useState('');39const [sorting, setSorting] = useState<MRT_SortingState>([]);40const [pagination, setPagination] = useState<MRT_PaginationState>({41pageIndex: 0,42pageSize: 10,43});4445//if you want to avoid useEffect, look at the React Query example instead46useEffect(() => {47const fetchData = async () => {48if (!data.length) {49setIsLoading(true);50} else {51setIsRefetching(true);52}5354const url = new URL(55'/api/data',56process.env.NODE_ENV === 'production'57? 'https://www.mantine-react-table.com'58: 'http://localhost:3001',59);60url.searchParams.set(61'start',62`${pagination.pageIndex * pagination.pageSize}`,63);64url.searchParams.set('size', `${pagination.pageSize}`);65url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));66url.searchParams.set('globalFilter', globalFilter ?? '');67url.searchParams.set('sorting', JSON.stringify(sorting ?? []));6869try {70const response = await fetch(url.href);71const json = (await response.json()) as UserApiResponse;72setData(json.data);73setRowCount(json.meta.totalRowCount);74} catch (error) {75setIsError(true);76console.error(error);77return;78}79setIsError(false);80setIsLoading(false);81setIsRefetching(false);82};83fetchData();84// eslint-disable-next-line react-hooks/exhaustive-deps85}, [86columnFilters, //refetch when column filters change87globalFilter, //refetch when global filter changes88pagination.pageIndex, //refetch when page index changes89pagination.pageSize, //refetch when page size changes90sorting, //refetch when sorting changes91]);9293const columns = useMemo<MRT_ColumnDef<User>[]>(94() => [95{96accessorKey: 'firstName',97header: 'First Name',98},99100{101accessorKey: 'lastName',102header: 'Last Name',103},104{105accessorKey: 'address',106header: 'Address',107},108{109accessorKey: 'state',110header: 'State',111},112{113accessorKey: 'phoneNumber',114header: 'Phone Number',115},116],117[],118);119120const table = useMantineReactTable({121columns,122data,123enableRowSelection: true,124getRowId: (row) => row.phoneNumber,125initialState: { showColumnFilters: true },126manualFiltering: true,127manualPagination: true,128manualSorting: true,129rowCount,130onColumnFiltersChange: setColumnFilters,131onGlobalFilterChange: setGlobalFilter,132onPaginationChange: setPagination,133onSortingChange: setSorting,134state: {135columnFilters,136globalFilter,137isLoading,138pagination,139showAlertBanner: isError,140showProgressBars: isRefetching,141sorting,142},143mantineToolbarAlertBannerProps: isError144? { color: 'red', children: 'Error loading data' }145: undefined,146});147148return <MantineReactTable table={table} />;149};150151export default Example;
1import { useEffect, useMemo, useState } from 'react';2import { MantineReactTable, useMantineReactTable } from 'mantine-react-table';34const Example = () => {5//data and fetching state6const [data, setData] = useState([]);7const [isError, setIsError] = useState(false);8const [isLoading, setIsLoading] = useState(false);9const [isRefetching, setIsRefetching] = useState(false);10const [rowCount, setRowCount] = useState(0);1112//table state13const [columnFilters, setColumnFilters] = useState([]);14const [globalFilter, setGlobalFilter] = useState('');15const [sorting, setSorting] = useState([]);16const [pagination, setPagination] = useState({17pageIndex: 0,18pageSize: 10,19});2021//if you want to avoid useEffect, look at the React Query example instead22useEffect(() => {23const fetchData = async () => {24if (!data.length) {25setIsLoading(true);26} else {27setIsRefetching(true);28}2930const url = new URL(31'/api/data',32process.env.NODE_ENV === 'production'33? 'https://www.mantine-react-table.com'34: 'http://localhost:3001',35);36url.searchParams.set(37'start',38`${pagination.pageIndex * pagination.pageSize}`,39);40url.searchParams.set('size', `${pagination.pageSize}`);41url.searchParams.set('filters', JSON.stringify(columnFilters ?? []));42url.searchParams.set('globalFilter', globalFilter ?? '');43url.searchParams.set('sorting', JSON.stringify(sorting ?? []));4445try {46const response = await fetch(url.href);47const json = await response.json();48setData(json.data);49setRowCount(json.meta.totalRowCount);50} catch (error) {51setIsError(true);52console.error(error);53return;54}55setIsError(false);56setIsLoading(false);57setIsRefetching(false);58};59fetchData();60// eslint-disable-next-line react-hooks/exhaustive-deps61}, [62columnFilters, //refetch when column filters change63globalFilter, //refetch when global filter changes64pagination.pageIndex, //refetch when page index changes65pagination.pageSize, //refetch when page size changes66sorting, //refetch when sorting changes67]);6869const columns = useMemo(70() => [71{72accessorKey: 'firstName',73header: 'First Name',74},7576{77accessorKey: 'lastName',78header: 'Last Name',79},80{81accessorKey: 'address',82header: 'Address',83},84{85accessorKey: 'state',86header: 'State',87},88{89accessorKey: 'phoneNumber',90header: 'Phone Number',91},92],93[],94);9596const table = useMantineReactTable({97columns,98data,99enableRowSelection: true,100getRowId: (row) => row.phoneNumber,101initialState: { showColumnFilters: true },102manualFiltering: true,103manualPagination: true,104manualSorting: true,105rowCount,106onColumnFiltersChange: setColumnFilters,107onGlobalFilterChange: setGlobalFilter,108onPaginationChange: setPagination,109onSortingChange: setSorting,110state: {111columnFilters,112globalFilter,113isLoading,114pagination,115showAlertBanner: isError,116showProgressBars: isRefetching,117sorting,118},119mantineToolbarAlertBannerProps: isError120? { color: 'red', children: 'Error loading data' }121: undefined,122});123124return <MantineReactTable table={table} />;125};126127export default Example;
1import {2type MRT_ColumnFiltersState,3type MRT_SortingState,4} from 'mantine-react-table';5import { type NextApiRequest, type NextApiResponse } from 'next';6import { getData } from './mock';78//This is just a simple mock of a backend API where you would do server-side pagination, filtering, and sorting9//You would most likely want way more validation and error handling than this in a real world application10//Also most of this logic should actually be in the database query itself, but this is just a mock11export default function handler(req: NextApiRequest, res: NextApiResponse) {12let dbData = getData();13const { start, size, filters, filterModes, sorting, globalFilter } =14req.query as Record<string, string>;1516const parsedFilterModes = JSON.parse(filterModes ?? '{}') as Record<17string,18string19>;2021const parsedColumnFilters = JSON.parse(filters) as MRT_ColumnFiltersState;22if (parsedColumnFilters?.length) {23parsedColumnFilters.map((filter) => {24const { id: columnId, value: filterValue } = filter;25const filterMode = parsedFilterModes?.[columnId] ?? 'contains';26dbData = dbData.filter((row) => {27const rowValue = row[columnId]?.toString()?.toLowerCase();28if (filterMode === 'contains') {29return rowValue.includes?.((filterValue as string).toLowerCase());30} else if (filterMode === 'startsWith') {31return rowValue.startsWith?.((filterValue as string).toLowerCase());32} else if (filterMode === 'endsWith') {33return rowValue.endsWith?.((filterValue as string).toLowerCase());34}35});36});37}3839if (globalFilter) {40dbData = dbData.filter((row) =>41Object.keys(row).some(42(columnId) =>43row[columnId]44?.toString()45?.toLowerCase()46?.includes?.((globalFilter as string).toLowerCase()),47),48);49}5051const parsedSorting = JSON.parse(sorting) as MRT_SortingState;52if (parsedSorting?.length) {53const sort = parsedSorting[0];54const { id, desc } = sort;55dbData.sort((a, b) => {56if (desc) {57return a[id] < b[id] ? 1 : -1;58}59return a[id] > b[id] ? 1 : -1;60});61}6263res.status(200).json({64data:65dbData?.slice(parseInt(start), parseInt(start) + parseInt(size)) ?? [],66meta: { totalRowCount: dbData.length },67});68}
View Extra Storybook Examples