Skip to content

Search Table

Add global search functionality to quickly find data across all columns.

Open in
Preview with Controlled State
Open in

The Search Table adds a global search input that filters data across all columns. Users can quickly find what they’re looking for without complex filters.

  1. Add the required components:
npx shadcn@latest add table input button dropdown-menu
  1. Add tanstack/react-table dependency:
npm install @tanstack/react-table
  1. Copy the DataTable components into your project. See the Installation Guide for detailed instructions.

We are going to build a table to show customers. Here’s what our data looks like:

type Customer = {
id: string
name: string
email: string
company: string
phone: string
}
const data: Customer[] = [
{
id: "1",
name: "John Doe",
company: "Acme Corp",
phone: "+1 234-567-8900",
},
// ...
]

Let’s start by building a table with global search.

First, we’ll define our columns.

columns.tsx
"use client"
import {
DataTableColumnHeader,
DataTableColumnTitle,
DataTableColumnSortMenu,
} from "@/components/niko-table/components"
import type { DataTableColumnDef } from "@/components/niko-table/types"
export type Customer = {
id: string
name: string
email: string
company: string
phone: string
}
export const columns: DataTableColumnDef<Customer>[] = [
{
accessorKey: "name",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Name" },
},
{
accessorKey: "email",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Email" },
},
{
accessorKey: "company",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Company" },
},
{
accessorKey: "phone",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Phone" },
},
]

Next, we’ll add the search filter component.

search-table.tsx
"use client"
import {
DataTableRoot,
DataTable,
DataTableHeader,
DataTableBody,
} from "@/components/niko-table/core"
import {
DataTableToolbarSection,
DataTablePagination,
DataTableSearchFilter,
DataTableColumnHeader,
DataTableColumnTitle,
DataTableColumnSortMenu,
} from "@/components/niko-table/components"
import type { DataTableColumnDef } from "@/components/niko-table/types"
type Customer = {
id: string
name: string
email: string
company: string
phone: string
}
const columns: DataTableColumnDef<Customer>[] = [
{
accessorKey: "name",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Name" },
},
{
accessorKey: "email",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Email" },
},
{
accessorKey: "company",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Company" },
},
{
accessorKey: "phone",
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Phone" },
},
]
export function SearchTable({ data }: { data: Customer[] }) {
return (
<DataTableRoot
data={data}
columns={columns}
config={{
enablePagination: true,
enableSorting: true,
enableFilters: true,
initialPageSize: 10,
}}
>
<DataTableToolbarSection>
<DataTableSearchFilter placeholder="Search customers..." />
</DataTableToolbarSection>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
<DataTablePagination />
</DataTableRoot>
)
}

The global search filter works by:

  1. Converting all searchable column values to strings
  2. Searching for the query across all columns
  3. Returning rows where ANY column matches
  4. Case-insensitive matching by default

By default, all columns are searchable. To exclude columns:

{
accessorKey: "id",
header: "ID",
enableColumnFilter: false, // Not searchable
}

Manage search state externally:

import { useState } from "react"
export function ControlledSearchTable({ data }: { data: Customer[] }) {
const [searchValue, setSearchValue] = useState("")
return (
<DataTableRoot
data={data}
columns={columns}
config={{
enableFilters: true,
}}
onGlobalFilterChange={setSearchValue}
>
<DataTableToolbarSection>
<DataTableSearchFilter
placeholder="Search customers..."
value={searchValue}
onChange={setSearchValue}
/>
</DataTableToolbarSection>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
<DataTablePagination />
</DataTableRoot>
)
}

Build your own search UI using the context:

import { useDataTable } from "@/components/niko-table/core"
import { Input } from "@/components/ui/input"
import { Search, X } from "lucide-react"
import { Button } from "@/components/ui/button"
function CustomSearchInput() {
const { table } = useDataTable<Customer>()
const globalFilter = table.getState().globalFilter
return (
<div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
value={(globalFilter as string) ?? ""}
onChange={e => table.setGlobalFilter(e.target.value)}
placeholder="Search..."
className="pr-9 pl-9"
/>
{globalFilter && (
<Button
variant="ghost"
size="sm"
className="absolute top-1/2 right-1 h-7 w-7 -translate-y-1/2 p-0"
onClick={() => table.setGlobalFilter("")}
>
<X className="h-4 w-4" />
</Button>
)}
</div>
)
}

✅ Use Search Table when:

  • Users need quick, simple searching
  • Data is text-heavy (names, descriptions, etc.)
  • You want a clean, minimal UI
  • Dataset is < 1000 rows (client-side)

❌ Consider other options when:

  • You need column-specific filters (use Advanced Table)
  • Dataset is > 1000 rows (implement server-side filtering)
  • Search is not the primary use case (use Basic Table)