react-datatable
Customization

Customizing filter UI

Use this page when the default filtering behavior is right, but the filtering surface still needs to feel more like your product.

The safest path is to customize the existing filter experience first, then replace deeper UI only when the shipped surface no longer matches the workflow.

Media placeholder: Filter button plus popover with product-specific filter labels and options

Show the toolbar filter button, searchable filter list, text-list/date submenus, and applied chips using realistic product wording.

Start by separating filter behavior from filter UI

A useful boundary is:

  • the column contract decides which fields are filterable and what filter types they use
  • the filter UI decides how those filters are presented to the user
  • the backend or local row model decides how filter payloads actually affect results

That means many product-specific filter changes do not require replacing core filtering logic.

Customize the names users see first

The filter picker uses the column's filter-facing name before it falls back to the header.

That makes meta.filterName the first place to tune the labels shown in the interface.

const columns: DatatableColumn<Customer>[] = [
  {
    id: "owner",
    accessorKey: "ownerName",
    header: "Owner",
    filterType: "text-list",
    meta: {
      filterName: "Account owner",
    },
    filterOptions: {
      options: owners.map((owner) => ({ value: owner.id, label: owner.name })),
    },
  },
]

Use this when the visible header is short but the filter picker needs clearer task-oriented language.

Use filter icons and option labels to match product language

The built-in filter list also supports column-level icon and label customization through metadata and options.

{
  id: "status",
  accessorKey: "status",
  header: "Status",
  filterType: "text-list",
  meta: {
    filterName: "Customer status",
    filterIcon: StatusIcon,
  },
  filterOptions: {
    options: [
      { value: "active", label: "Active customer" },
      { value: "trial", label: "Trial account" },
      { value: "paused", label: "Paused" },
    ],
  },
}

This is usually enough to make the filter surface feel product-aware without touching the underlying components.

For option-list filters, option rows can also render custom React. Use filterOptions.renderOption when a plain label is not enough:

{
  id: "owner",
  accessorFn: (row) => row.owner.name,
  header: "Owner",
  filterType: "text-list",
  filterOptions: {
    options: owners.map((owner) => ({ value: owner.name, label: owner.name })),
    renderOption: (option) => {
      const owner = owners.find((item) => item.name === option.value)

      return (
        <>
          {owner ? <OwnerAvatar owner={owner} /> : null}
          <span className="truncate">{option.label}</span>
        </>
      )
    },
  },
}

This only changes option presentation. Filtering still uses the selected option values. renderOption is available for text-list, id-list, and boolean filters.

Pick filter types that produce the UI you want

The built-in filter picker currently branches by filterType.

In practice that means:

  • text and number open through the modal editor flow
  • text-list and date open through inline submenu editors
  • only implemented filter types appear in the searchable filter list

If you want a compact pick-list experience, text-list is often the best fit. If you want freeform matching, use text. If you want range or comparison editing, use number or date.

The UI shape is part of the filter-type choice, not just the backend payload choice.

Keep the toolbar entry point aligned with the table's scope

The toolbar's filter button is the main entry point for column filtering.

<Datatable
  tableKey="customers"
  columns={columns}
  data={rows}
  toolbar={{
    filterButton: true,
    appliedState: {
      showFilters: true,
    },
  }}
/>

If you turn on filtering, leave applied chips visible unless the table is intentionally minimalist. The built-in experience works best when users can both open filters and see active filters in the same surface.

Use applied chips as part of the UI design, not an afterthought

Filter UI includes both the editor and the visible state it leaves behind.

The applied-state bar is where users confirm, remove, and compare active filters over time.

That means filter customization should include:

  • clear filter names
  • human-readable option labels
  • a toolbar layout that leaves enough room for the applied-state bar to matter

If the filter picker is polished but the applied chips are confusing, the experience still feels unfinished.

Use the shipped UI when it is enough

The default filter UI is a good fit when you need to customize:

  • filter labels
  • icons
  • option-list labels
  • option-list rows with renderOption
  • which columns appear in the picker
  • whether filter chips are visible

In those cases, stay within the built-in surface. It is cheaper, more consistent, and easier to maintain.

Move from configuration to replacement when you need to

You are probably beyond simple configuration when the product needs:

  • a radically different filter workflow than the searchable picker
  • custom editors for filter types the built-in UI does not implement
  • filter controls embedded somewhere other than the toolbar/applied-state pattern
  • a domain-specific interaction model that does not map cleanly to the current modal/submenu flows

At that point, you are designing a different filtering product surface.

Be honest about partially implemented filter types

The type system includes custom, but the built-in UI is strongest for:

  • text
  • text-list
  • number
  • date
  • boolean
  • id-list

Treat custom as an extension seam unless you are also building the editor experience yourself.

Use this customization ladder

When filter UI needs work, move in this order:

  1. choose better filterType values
  2. improve meta.filterName, meta.filterIcon, and option labels
  3. tune which columns are filterable at all
  4. keep applied chips visible and understandable
  5. only then consider replacing deeper UI components

That sequence usually gets the product where it needs to go with much less complexity.

Verify the filter surface in context

Before you call the filter UI done, check that:

  • the filter picker uses the words your users expect
  • list-style filters have clear human labels
  • the modal versus submenu behavior matches the kind of question each filter asks
  • active chips are understandable after the picker closes
  • the UI does not imply support for filter types you have not actually implemented

Where to go next

On this page