Skip to content

Search Table

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

Wires DataTableSearchFilter to a single input that fuzzy-matches across every visible column at once, using TanStack Table’s built-in globalFilter. A drop-in for any table where users mostly want a “just find this thing” affordance and don’t need per-column filter controls.

Open in
Loading data...
Name
Email
Company
Phone
Preview with Controlled State
Open in
Loading data...
Name
Email
Company
Phone
Search Table State
Live view of the search table state with loading and data management
Loading State:Loading...
Search Query:None
Total Customers:0
Filtered Results:0
Unique Companies:0
Email Domains:0
Sorting:None
Page:1 (Size: 5)
Hidden Columns:0
View Full State Object
Loading State:
{
  "isLoading": true,
  "dataLength": 0
}
Global Filter:
""
Sorting:
[]
Pagination:
{
  "pageIndex": 0,
  "pageSize": 5
}
Column Visibility:
{}

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.

Install the DataTable core and add-ons for this example:

pnpm dlx shadcn@latest add @niko-table/data-table @niko-table/data-table-search-filter @niko-table/data-table-pagination @niko-table/data-table-view-menu @niko-table/data-table-column-sort

First time using @niko-table? See the Installation Guide to set up the registry.

For other add-ons or manual copy-paste, see the Installation Guide.

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",
email: "john@example.com",
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 } from "@/components/niko-table/components/data-table-column-header"
import { DataTableColumnTitle } from "@/components/niko-table/components/data-table-column-title"
import { DataTableColumnSortMenu } from "@/components/niko-table/components/data-table-column-sort"
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 } from "@/components/niko-table/core/data-table-root"
import { DataTable } from "@/components/niko-table/core/data-table"
import {
DataTableHeader,
DataTableBody,
} from "@/components/niko-table/core/data-table-structure"
import { DataTableToolbarSection } from "@/components/niko-table/components/data-table-toolbar-section"
import { DataTablePagination } from "@/components/niko-table/components/data-table-pagination"
import { DataTableSearchFilter } from "@/components/niko-table/components/data-table-search-filter"
import { DataTableColumnHeader } from "@/components/niko-table/components/data-table-column-header"
import { DataTableColumnTitle } from "@/components/niko-table/components/data-table-column-title"
import { DataTableColumnSortMenu } from "@/components/niko-table/components/data-table-column-sort"
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/data-table-context"
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)