Examples › Searching and filtering
Adjust the array of records
you’re feeding to Mantine DataTable based on your own logic in order to perform searching and filtering.
In order to support column-based filtering you can use the filter
and filtering
column properties.
Here’s a possible (rather naive and rough around the edges) implementation:
Name | Department name | Company | Birth date | Age |
---|---|---|---|---|
Jerald Howell | Industrial | Runte Inc | May 21 1950 | 74 |
Kathleen Ruecker | Computers | Shanahan, Robel and Beier | Dec 19 1943 | 81 |
Erica Volkman | Toys | Goyette Inc | Jan 29 1955 | 69 |
Clifford Oberbrunner | Automotive | Rau - O'Hara | Mar 06 1979 | 45 |
Alison Kling | Jewelery | Goyette Inc | Jan 27 1997 | 27 |
Sue Zieme | Books | Cummerata - Kuhlman | Sep 12 1960 | 64 |
Felicia Gleason | Shoes | Doyle, Hodkiewicz and O'Connell | Mar 09 1977 | 47 |
Alfredo Zemlak | Games | Runte Inc | Nov 12 1988 | 36 |
Emily Bartoletti | Automotive | Rau - O'Hara | Jan 05 1968 | 56 |
Delores Reynolds | Industrial | Runte Inc | Jun 04 2004 | 20 |
Louis Schamberger | Tools | Cummerata - Kuhlman | Sep 07 1994 | 30 |
Beverly Heller | Beauty | Runte Inc | Nov 29 2001 | 23 |
Eugene Feest | Kids | Goyette Inc | Feb 20 1954 | 70 |
Martin Bahringer | Beauty | Rau - O'Hara | May 26 1945 | 79 |
Ellis Miller | Electronics | Connelly, Feest and Hodkiewicz | May 24 1950 | 74 |
Gloria Cole | Home | Doyle, Hodkiewicz and O'Connell | Dec 09 1996 | 28 |
Linda Witting | Baby | Doyle, Hodkiewicz and O'Connell | May 16 1965 | 59 |
Gregg Kutch | Movies | Shanahan, Robel and Beier | Nov 28 1986 | 38 |
Mamie Raynor | Grocery | Cummerata - Kuhlman | Nov 05 1992 | 32 |
Erick Bruen | Electronics | Goyette Inc | Sep 26 1952 | 72 |
Faith Langworth | Clothing | Runte Inc | Nov 27 1983 | 41 |
Alicia Leannon | Sports | Feest, Bogan and Herzog | Dec 27 2005 | 18 |
Boyd Mohr | Jewelery | Goyette Inc | Jun 18 1951 | 73 |
Lindsey Heidenreich | Games | Shanahan, Robel and Beier | Jul 13 1997 | 27 |
Elsa Marvin | Books | Tillman - Jacobi | Oct 07 1982 | 42 |
Debbie Hagenes | Clothing | Runte Inc | Jan 24 1968 | 56 |
Lionel McCullough | Kids | Goldner, Rohan and Lehner | Feb 23 1986 | 38 |
Kim Lebsack | Jewelery | Cummerata - Kuhlman | Jun 11 1970 | 54 |
Rolando Weissnat | Home | Rau - O'Hara | Apr 25 1951 | 73 |
Jacqueline Lesch | Games | Runte Inc | Jul 22 1978 | 46 |
Felix Stokes | Kids | Goyette Inc | Mar 21 1984 | 40 |
Renee Tillman | Industrial | Goldner, Rohan and Lehner | Nov 24 1953 | 71 |
Richard Watsica | Outdoors | Runte Inc | Jun 10 1971 | 53 |
Nathan Wolf | Games | Goldner, Rohan and Lehner | Jul 02 1998 | 26 |
Jonathan Keebler-Crona | Music | Goyette Inc | Oct 22 2002 | 22 |
Kathleen Spinka | Jewelery | Goldner, Rohan and Lehner | Feb 28 1947 | 77 |
Bernice Schinner | Garden | Tillman - Jacobi | Dec 23 2005 | 18 |
Adam Pagac | Books | Doyle, Hodkiewicz and O'Connell | Dec 04 1961 | 63 |
Earl Ryan | Beauty | Rau - O'Hara | Jan 08 1961 | 63 |
Greg Bailey | Home | Rau - O'Hara | Jan 02 1967 | 57 |
Anne Powlowski | Music | Connelly, Feest and Hodkiewicz | Jan 26 1957 | 67 |
Abraham Dooley | Jewelery | Tillman - Jacobi | Oct 05 1956 | 68 |
Myron Lemke | Shoes | Feest, Bogan and Herzog | Sep 27 1978 | 46 |
Dianna Gislason-Lesch | Games | Cummerata - Kuhlman | May 29 1966 | 58 |
Reginald Hagenes | Industrial | Runte Inc | May 19 1950 | 74 |
Shelia Turcotte | Music | Goyette Inc | Jul 22 1959 | 65 |
Carlton Jenkins | Sports | Runte Inc | Jun 07 1943 | 81 |
Lance Wiegand | Grocery | Cummerata - Kuhlman | Jul 12 1966 | 58 |
Ruby Graham | Sports | Feest, Bogan and Herzog | May 30 1970 | 54 |
Hattie Collier | Outdoors | Goyette Inc | Apr 14 1954 | 70 |
Viola Rath | Movies | Runte Inc | Mar 24 1970 | 54 |
Roland Huel | Jewelery | Doyle, Hodkiewicz and O'Connell | Apr 20 1973 | 51 |
Leticia Wiegand | Kids | Goyette Inc | Jan 13 1958 | 66 |
Jacqueline Kulas | Beauty | Feest, Bogan and Herzog | Aug 30 1960 | 64 |
Cristina Jaskolski | Sports | Feest, Bogan and Herzog | Jan 25 1966 | 58 |
Felipe Daugherty | Music | Connelly, Feest and Hodkiewicz | Aug 12 1969 | 55 |
Timothy Heaney | Kids | Doyle, Hodkiewicz and O'Connell | Dec 05 2005 | 19 |
Alonzo Kutch | Sports | Tillman - Jacobi | May 02 1982 | 42 |
Keith Kling | Sports | Goyette Inc | Feb 16 1961 | 63 |
Janice Goyette | Sports | Runte Inc | Mar 14 1963 | 61 |
Helen Kunze-MacGyver | Jewelery | Doyle, Hodkiewicz and O'Connell | Feb 11 2002 | 22 |
Barry Jast | Grocery | Runte Inc | May 09 1978 | 46 |
Ramiro Cummings | Sports | Goyette Inc | Apr 09 1966 | 58 |
Antonio Little-Bahringer | Games | Shanahan, Robel and Beier | Nov 21 1988 | 36 |
Samuel Zemlak | Electronics | Goyette Inc | Apr 28 1964 | 60 |
Doris Emard | Games | Runte Inc | May 04 2003 | 21 |
Olivia Abernathy | Outdoors | Runte Inc | Apr 30 1969 | 55 |
Justin Kohler | Health | Goyette Inc | Jan 11 1980 | 44 |
Scott Oberbrunner | Sports | Goyette Inc | Jan 22 1997 | 27 |
Yolanda Spinka | Music | Connelly, Feest and Hodkiewicz | Jan 19 1981 | 43 |
Brad Ullrich-Orn | Games | Feest, Bogan and Herzog | Aug 08 1952 | 72 |
Gloria Fisher | Health | Goyette Inc | Nov 20 1995 | 29 |
Sergio Crist | Books | Goldner, Rohan and Lehner | May 31 1995 | 29 |
Theresa Sporer | Sports | Goyette Inc | May 15 2003 | 21 |
Theodore Wiegand | Games | Cummerata - Kuhlman | May 18 2000 | 24 |
Rudy Rowe | Grocery | Tillman - Jacobi | Oct 01 1962 | 62 |
Kurt Raynor | Toys | Goyette Inc | Aug 20 1957 | 67 |
Ruth Medhurst | Home | Doyle, Hodkiewicz and O'Connell | Jan 09 1947 | 77 |
Tim Abernathy | Shoes | Doyle, Hodkiewicz and O'Connell | Oct 23 1984 | 40 |
Rebecca Runolfsdottir | Grocery | Runte Inc | Jul 07 1964 | 60 |
Angel Aufderhar | Health | Runte Inc | Jun 22 1959 | 65 |
Javier Bergstrom | Movies | Cummerata - Kuhlman | Oct 20 1993 | 31 |
Leigh Klocko | Music | Rau - O'Hara | Jun 14 1955 | 69 |
Taylor Rice | Beauty | Goldner, Rohan and Lehner | Sep 10 1948 | 76 |
Cindy Goldner | Shoes | Doyle, Hodkiewicz and O'Connell | Nov 30 1978 | 46 |
Shannon Crist | Outdoors | Connelly, Feest and Hodkiewicz | Nov 12 1994 | 30 |
Sarah Maggio | Books | Goyette Inc | Nov 09 2000 | 24 |
Carlton Langosh-Glover | Baby | Runte Inc | Dec 06 1966 | 58 |
Felicia Roob | Games | Doyle, Hodkiewicz and O'Connell | Dec 21 1968 | 55 |
Simon Kuhic | Kids | Goldner, Rohan and Lehner | May 07 1958 | 66 |
Sharon Daniel | Music | Runte Inc | Jul 05 1999 | 25 |
Erin Bayer | Books | Tillman - Jacobi | Jul 04 1982 | 42 |
Erika Powlowski-Corwin | Sports | Connelly, Feest and Hodkiewicz | Oct 10 1949 | 75 |
Vicky Pollich | Books | Cummerata - Kuhlman | May 22 1986 | 38 |
Felicia Ziemann | Industrial | Runte Inc | Dec 15 1991 | 33 |
Colleen Crooks | Automotive | Rau - O'Hara | Jun 06 1998 | 26 |
Jimmy Fisher | Kids | Goyette Inc | Dec 10 1945 | 79 |
Lula Reichert | Games | Cummerata - Kuhlman | Mar 17 1995 | 29 |
Bethany Schinner | Movies | Cummerata - Kuhlman | Nov 12 1951 | 73 |
Allen Hackett | Health | Tillman - Jacobi | May 07 1984 | 40 |
No records
The code for this example is as follows:
'use client';
import { ActionIcon, Button, Checkbox, MultiSelect, Stack, TextInput } from '@mantine/core';
import { DatePicker, type DatesRangeValue } from '@mantine/dates';
import { useDebouncedValue } from '@mantine/hooks';
import { IconSearch, IconX } from '@tabler/icons-react';
import { DataTable } from 'mantine-datatable';
import dayjs from 'dayjs';
import { useEffect, useMemo, useState } from 'react';
import { employees } from '~/data';
const initialRecords = employees.slice(0, 100);
export function SearchingAndFilteringExample() {
const [records, setRecords] = useState(initialRecords);
const departments = useMemo(() => {
const departments = new Set(employees.map((e) => e.department.name));
return [...departments];
}, []);
const [query, setQuery] = useState('');
const [selectedDepartments, setSelectedDepartments] = useState<string[]>([]);
const [birthdaySearchRange, setBirthdaySearchRange] = useState<DatesRangeValue>();
const [seniors, setSeniors] = useState(false);
const [debouncedQuery] = useDebouncedValue(query, 200);
useEffect(() => {
setRecords(
initialRecords.filter(({ firstName, lastName, department, birthDate }) => {
if (
debouncedQuery !== '' &&
!`${firstName} ${lastName}`.toLowerCase().includes(debouncedQuery.trim().toLowerCase())
)
return false;
if (
birthdaySearchRange &&
birthdaySearchRange[0] &&
birthdaySearchRange[1] &&
(dayjs(birthdaySearchRange[0]).isAfter(birthDate, 'day') ||
dayjs(birthdaySearchRange[1]).isBefore(birthDate, 'day'))
)
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, birthdaySearchRange, selectedDepartments, seniors]);
return (
<DataTable
height={300}
withTableBorder
withColumnBorders
records={records}
columns={[
{
accessor: 'name',
render: ({ firstName, lastName }) => `${firstName} ${lastName}`,
filter: (
<TextInput
label="Employees"
description="Show employees whose names include the specified text"
placeholder="Search employees..."
leftSection={<IconSearch size={16} />}
rightSection={
<ActionIcon size="sm" variant="transparent" c="dimmed" onClick={() => setQuery('')}>
<IconX size={14} />
</ActionIcon>
}
value={query}
onChange={(e) => setQuery(e.currentTarget.value)}
/>
),
filtering: query !== '',
},
{
accessor: 'department.name',
filter: (
<MultiSelect
label="Departments"
description="Show all employees working at the selected departments"
data={departments}
value={selectedDepartments}
placeholder="Search departments…"
onChange={setSelectedDepartments}
leftSection={<IconSearch size={16} />}
comboboxProps={{ withinPortal: false }}
clearable
searchable
/>
),
filtering: selectedDepartments.length > 0,
},
{ accessor: 'department.company.name', title: 'Company' },
{
accessor: 'birthDate',
textAlign: 'right',
render: ({ birthDate }) => dayjs(birthDate).format('MMM DD YYYY'),
filter: ({ close }) => (
<Stack>
<DatePicker
maxDate={new Date()}
type="range"
value={birthdaySearchRange}
onChange={setBirthdaySearchRange}
/>
<Button
disabled={!birthdaySearchRange}
variant="light"
onClick={() => {
setBirthdaySearchRange(undefined);
close();
}}
>
Clear
</Button>
</Stack>
),
filtering: Boolean(birthdaySearchRange),
},
{
accessor: 'age',
textAlign: 'right',
render: ({ birthDate }) => dayjs().diff(birthDate, 'y'),
filter: () => (
<Checkbox
label="Seniors"
description="Show employees who are older than 70 years"
checked={seniors}
onChange={() => {
setSeniors((current) => !current);
}}
/>
),
},
]}
/>
);
}
To use the Mantine Component with a popover inside the filter column property, you need to render the child properties without using a Portal. Please refer to the Mantine documentation on Nested Popovers for more details.
Why no built-in “Excel-like” searching and filtering support?
You’ll often have to implement searching and filtering data in your projects.
However there’s simply no way for Mantine DataTable to accommodate every possible usage scenario out there. Most of the times you’d have to deal with pagination, sorting, asynchronous loading; sometimes you’d have to place a search box or custom filtering criteria selectors in the header of your entire website/application.
Not to mention that in real-life you’d most often do the actual filtering and/or searching in a server API.
The responsibilities and areas of control are most of the times spread across your application’s UI and architectural layers, and trying to fit all this in a standard component design and behavior would needlessly constrain the developer.
However there’s simply no way for Mantine DataTable to accommodate every possible usage scenario out there. Most of the times you’d have to deal with pagination, sorting, asynchronous loading; sometimes you’d have to place a search box or custom filtering criteria selectors in the header of your entire website/application.
Not to mention that in real-life you’d most often do the actual filtering and/or searching in a server API.
The responsibilities and areas of control are most of the times spread across your application’s UI and architectural layers, and trying to fit all this in a standard component design and behavior would needlessly constrain the developer.
Head over to the next example to discover more features.