Mantine DataTable
website·Indexed 30 days ago
https://icflorescu.github.io/mantine-datatable/A data table component for Mantine featuring support for asynchronous data loading, record selection, expanding rows, searching, filtering, and nested tables. Includes documentation on component properties and various implementation examples for v6.
What's inside Mantine DataTable
- mantine-contextmenu is a lightweight, TypeScript-based library for adding desktop-grade context menus to React applications built with Mantine V9.x. It is compatible with modern frameworks like Next.js, Vite, Create React App, Remix, and Gatsby. The library respects Mantine's color scheme (dark/light mode) out of the box and allows for custom content overrides instead of using default generated menu items.
Overview of Mantine ContextMenu for Mantine V6
Mantine ContextMenu is a lightweight, TypeScript-based library for adding desktop-grade context menus to React applications using Mantine V6. It supports dark mode, is customizable via custom content, and is compatible with frameworks like Next.js, Vite, Create React App, Remix, and Gatsby.Overview of Mantine DataTable v6
Mantine DataTable is a lightweight, dependency-free React component designed for data-rich user interfaces. It is specifically built for applications using Mantine V6. Key features include:
- Asynchronous data loading
- Pagination and column sorting
- Multiple row selection (including Gmail-style additive batch selection)
- Custom cell data rendering
- Row context menus and row expansion
- Nested tables
- Full TypeScript support for type-safe development
It is compatible with modern React frameworks supported by Mantine, including Next.js, Vite, Create React App, Remix, and Gatsby.
Overview of Mantine DataTable
Mantine DataTable is a lightweight, dependency-free React component that provides datagrid-like functionality for data-rich user interfaces. It features built-in dark-mode support and Gmail-style additive batch row selection. The library is written in TypeScript, providing well-typed options and JSDoc documentation for type-safe development. It is released under the MIT license and is compatible with Mantine V9.x (with a separate version available for Mantine V6).Implement client-side searching and filtering in Mantine DataTable
Mantine DataTable does not provide built-in 'Excel-like' filtering because search and filter logic often depends on specific application architecture (e.g., server-side APIs, global search bars, or complex pagination). Instead, developers should implement filtering by manipulating the
recordsarray passed to the component.To create a filter UI within the table headers, use the
filterproperty in the column definition. This property accepts a React component (such asTextInput,MultiSelect, orCheckbox) that updates the state used to filter the data records.'use client'; import { DataTable } from 'mantine-datatable'; import { TextInput, MultiSelect, Checkbox } from '@mantine/core'; import { useDebouncedValue } from '@mantine/hooks'; import { useEffect, useState } from 'react'; import dayjs from 'dayjs'; export function SearchingAndFilteringExample() { const [records, setRecords] = useState(initialRecords); const [query, setQuery] = useState(''); const [selectedDepartments, setSelectedDepartments] = useState<string[]>([]); const [seniors, setSeniors] = useState(false); const [debouncedQuery] = useDebouncedValue(query, 200); // Filter the records based on state changes useEffect(() => { setRecords( initialRecords.filter(({ firstName, lastName, department, birthDate }) => { if (debouncedQuery !== '' && !`${firstName} ${lastName}`.toLowerCase().includes(debouncedQuery.trim().toLowerCase())) return false; if (selectedDepartments.length && !selectedDepartments.some((d) => d === department.name)) return false; if (seniors && dayjs().diff(birthDate, 'y') < 70) return false; return true; }) ); }, [debouncedQuery, selectedDepartments, seniors]); return ( <DataTable records={records} columns={[ { accessor: 'name', render: ({ firstName, lastName }) => `${firstName} ${lastName}`, filter: ( <TextInput label="Employees" value={query} onChange={(e) => setQuery(e.currentTarget.value)} /> ), filtering: query !== '', }, { accessor: 'department.name', filter: ( <MultiSelect label="Departments" data={['Engineering', 'Sales', 'HR']} value={selectedDepartments} onChange={setSelectedDepartments} /> ), filtering: selectedDepartments.length > 0, }, { accessor: 'age', filter: () => ( <Checkbox label="Seniors" checked={seniors} onChange={() => setSeniors((current) => !current)} /> ), }, ]} /> ); }Report bugs and request features for Mantine DataTable
To report a bug, open an issue on the GitHub repository. For new feature ideas or missing functionality, use the GitHub Discussions section.Implement a complex data table with asynchronous loading and row expansion
This example demonstrates how to integratemantine-datatablewith@tanstack/react-queryfor server-side pagination and sorting, while implementing advanced UI features like row expansion and custom action columns.'use client'; import { DataTable, type DataTableColumn, type DataTableProps, type DataTableSortStatus } from 'mantine-datatable'; import { useQuery } from '@tanstack/react-query'; import { useState, useCallback } from 'react'; import { ActionIcon, Group, TextInput, Button } from '@mantine/core'; import { openModal, closeAllModals } from '@mantine/modals'; const PAGE_SIZE = 100; export function ComplexUsageExample() { const [page, setPage] = useState(1); const [sortStatus, setSortStatus] = useState<DataTableSortStatus<Employee>>({ columnAccessor: 'name', direction: 'asc', }); const [selectedRecords, setSelectedRecords] = useState<Employee[]>([]); // Asynchronous data fetching const { data, isFetching } = useQuery({ queryKey: ['employees', sortStatus.columnAccessor, sortStatus.direction, page], queryFn: () => getEmployeesAsync({ recordsPerPage: PAGE_SIZE, page, sortStatus, delay: { min: 300, max: 500 } }), }); // Custom column definitions const columns: DataTableProps<Employee>['columns'] = [ { accessor: 'name', sortable: true, render: ({ firstName, lastName }) => `${firstName} ${lastName}`, }, { accessor: 'department.company.name', title: 'Company', sortable: true, visibleMediaQuery: '(min-width: 48em)', // Example breakpoint }, { accessor: 'actions', title: 'Actions', width: '0%', render: (record) => ( <Group gap={4} justify="right" wrap="nowrap"> <ActionIcon onClick={(e) => { e.stopPropagation(); // Prevent triggering row click openModal({ title: `Message ${record.firstName}`, children: <TextInput placeholder="Your message..." />, }); }} > <IconMessage size={16} /> </ActionIcon> </Group> ), }, ]; // Row expansion configuration const rowExpansion: DataTableProps<Employee>['rowExpansion'] = { allowMultiple: true, content: ({ record }) => ( <div style={{ padding: '10px' }}> Detailed info for {record.firstName} {record.lastName} </div> ), }; return ( <DataTable columns={columns} records={data?.employees} totalRecords={data?.total} fetching={isFetching} page={page} onPageChange={setPage} recordsPerPage={PAGE_SIZE} sortStatus={sortStatus} onSortStatusChange={(status) => { setPage(1); setSortStatus(status); }} selectedRecords={selectedRecords} onSelectedRecordsChange={setSelectedRecords} rowExpansion={rowExpansion} highlightOnHover withTableBorder striped /> ); }Report bugs or suggest features for Mantine ContextMenu
Bugs should be reported by raising an issue on the GitHub repository. New feature ideas and discussions should be handled via the GitHub Discussions tab.Implement column sorting in Mantine DataTable
To enable sorting in a Mantine DataTable, you must configure three things: set thesortableproperty totrueon the desired columns, provide asortStatusobject to track the current sorting state, and implement anonSortStatusChangehandler to update that state when a header is clicked. It is recommended to disable text selection on the table to prevent accidental text highlighting when users click column headers repeatedly.import sortBy from 'lodash/sortBy'; import { DataTable, DataTableSortStatus } from 'mantine-datatable'; import { useEffect, useState } from 'react'; import { companies, type Company } from '~/data'; export default function SortingExample() { // 1. Manage sort status state const [sortStatus, setSortStatus] = useState<DataTableSortStatus>({ columnAccessor: 'name', direction: 'asc', }); const [records, setRecords] = useState(sortBy(companies, 'name')); // 2. Handle data sorting logic based on sortStatus useEffect(() => { const data = sortBy(companies, sortStatus.columnAccessor) as Company[]; setRecords(sortStatus.direction === 'desc' ? data.reverse() : data); }, [sortStatus]); return ( <DataTable withBorder withColumnBorders records={records} columns={[ { accessor: 'name', width: '40%', sortable: true }, // 3. Mark column as sortable { accessor: 'streetAddress', width: '60%' }, { accessor: 'city', width: 160, sortable: true }, { accessor: 'state', sortable: true }, ]} sortStatus={sortStatus} onSortStatusChange={setSortStatus} /> ); }Implement nested tables with asynchronous data loading
Mantine DataTable'srowExpansion.contentfunction is lazily executed only when a row is expanded. This behavior allows developers to asynchronously load data for nested tables without creating unnecessary DOM elements or triggering API calls for every row on initial load. To implement this, create a separate component for the nested table that fetches its own data based on the ID of the parent record passed via thecontentfunction.import { DataTable } from 'mantine-datatable'; function ParentTable() { const [expandedRecordIds, setExpandedRecordIds] = useState<string[]>([]); return ( <DataTable records={companies} columns={[ { accessor: 'name', title: 'Company' }, { accessor: 'employees', title: 'Employees' }, ]} rowExpansion={{ allowMultiple: true, expanded: { recordIds: expandedRecordIds, onRecordIdsChange: setExpandedRecordIds }, // The content function is lazily executed upon expansion content: ({ record }) => <DepartmentsTable companyId={record.id} />, }} /> ); } function DepartmentsTable({ companyId }: { companyId: string }) { // Fetch data asynchronously based on the parent ID const { records, loading } = useDepartmentsAsync({ companyId }); return ( <DataTable noHeader fetching={loading} records={records} columns={[ { accessor: 'name', title: 'Department' }, ]} rowExpansion={{ allowMultiple: true, content: ({ record }) => <EmployeesTable departmentId={record.id} />, }} /> ); }Add a page size selector to Mantine DataTable
You can allow users to change the number of records displayed per page by adding a page size selector. This requires providing an array of available sizes and a callback to handle the size change.import { DataTable } from 'mantine-datatable'; import { useState } from 'react'; const PAGE_SIZES = [10, 15, 20]; export default function PageSizeExample() { const [pageSize, setPageSize] = useState(PAGE_SIZES[1]); const [page, setPage] = useState(1); return ( <DataTable records={records} columns={columns} totalRecords={employees.length} recordsPerPage={pageSize} page={page} onPageChange={setPage} recordsPerPageOptions={PAGE_SIZES} onRecordsPerPageChange={setPageSize} recordsPerPageLabel="Records per page" /> ); }Contribute code to Mantine DataTable
To contribute code to Mantine DataTable, you must use
pnpmfor dependency management and scripts. The project is built with Next.js (App Router) and React Server Components. The package source code is located in thepackagefolder, and the documentation website is in theappfolder.Key requirements for Pull Requests:
- Target the
nextbranch (PRs targetingmainwill be rejected). - Include example pages or update existing ones when implementing new features.
- Ensure code is formatted using the project's tooling.
# Install dependencies pnpm install # Start development server pnpm dev # Check for linting errors pnpm lint # Format code with Biome pnpm format # Build the project pnpm build- Target the