# How the refine-shadcn Table Component Implements Sorting, Filtering, and Pagination

> Discover how refine-shadcn Table component uses TanStack Table and react-table hooks for sorting, filtering, and pagination control. Learn about toggleSorting, setFilterValue, and setPageIndex methods.

- Repository: [Ferdi ÜNAL/refine-shadcn](https://github.com/ferdiunal/refine-shadcn)
- Tags: deep-dive
- Published: 2026-03-01

---

**The refine-shadcn Table component delegates sorting, filtering, and pagination logic to TanStack Table and `@refinedev/react-table` hooks, rendering Shadcn UI controls that invoke methods like `column.toggleSorting()`, `column.setFilterValue()`, and `table.setPageIndex()`.**

The refine-shadcn library provides a declarative Table component that wraps TanStack Table and integrates with the Refine data framework. By combining the `useTable` hook from `@refinedev/react-table` with dedicated UI field components, it enables server-side and client-side data manipulation through a unified interface.

## Sorting Implementation in refine-shadcn

### Enabling Sorting in Column Definitions

Sorting is controlled at the column level through the `enableSorting` property in column definitions. In [`packages/theme/src/table/index.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx), the Table component checks both global and column-specific sorting flags before rendering sort controls:

```tsx
{tableOptions.enableSorting &&
  columnDef.enableSorting && (
    <SortAction column={header.column} />
)}

```

*Source:* [`packages/theme/src/table/index.tsx#L93-L100`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx#L93-L100)

When both flags are true, the component injects a `SortAction` component into the header cell.

### The SortAction Component and Toggle Logic

The `SortAction` component, located in [`packages/theme/src/table/fields/sort.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/sort.tsx), renders interactive caret icons that cycle through sort states. Clicking the icon calls `column.toggleSorting()` with a boolean argument determining the next direction:

```tsx
onClick={() => {
  column?.toggleSorting(column?.getIsSorted() === "asc");
}}

```

*Source:* [`packages/theme/src/table/fields/sort.tsx#L10-L15`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/sort.tsx#L10-L15)

The component uses `column.getIsSorted()` to determine the current state and applies conditional styling—`text-foreground` for active sorting and `text-input` for inactive—providing immediate visual feedback.

### Integration with useTable Hook

The `useTable` hook from `@refinedev/react-table` maintains sorting state in `table.getState().sorting`. When `toggleSorting()` fires, the hook updates this state and triggers a data refetch if the table is bound to a Refine query. The UI automatically re-renders with updated `column.getIsSorted()` values, synchronizing the header icons with the underlying data order.

## Filtering Implementation Strategies

### Column Filter Configuration

The Table component determines filter eligibility by checking global and column-specific flags in [`packages/theme/src/table/index.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx):

```tsx
const isFilterable = Boolean(
  tableOptions.enableColumnFilters || tableOptions?.enableFilters,
);

```

*Source:* [`packages/theme/src/table/index.tsx#L58-L63`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx#L58-L63)

When a column definition includes a `filter` property, the component renders that specific filter UI:

```tsx
{isFilterable &&
  columnDef?.filter &&
  (columnDef.filter({
    column: header.column,
    title: `${columnDef.header} Filter`,
  }) as React.ReactNode)}

```

*Source:* [`packages/theme/src/table/index.tsx#L100-L106`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx#L100-L106)

### Filter Field Components

The refine-shadcn Table provides three primary filter implementations:

- **`TableFilterSearchColumn`**: Text-based search using `column.setFilterValue(e.target.value)`
- **`TableFilterDropdown`**: Multi-select dropdown using `column.setFilterValue(filterValues)`
- **`TableFilterDateRangePicker`**: Date range selection passing array values

The search filter implementation in [`packages/theme/src/table/fields/table-filter-search-column.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/table-filter-search-column.tsx) binds input changes directly to the column filter state:

```tsx
<Input
  defaultValue={selectedValue ?? ""}
  onChange={(e) => {
    column?.setFilterValue(e.target.value);
  }}
/>

```

*Source:* [`packages/theme/src/table/fields/table-filter-search-column.tsx#L66-L72`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/table-filter-search-column.tsx#L66-L72)

For dropdown filters, the component manages an array of selected values and clears the filter when empty:

```tsx
column?.setFilterValue(
  filterValues.length ? filterValues : undefined,
);

```

*Source:* [`packages/theme/src/table/fields/table-filter-dropdown.tsx#L22-L28`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/table-filter-dropdown.tsx#L22-L28)

### Filter State Management

`useTable` stores active filters in `table.getState().columnFilters`. Each `setFilterValue` call updates this array, triggering TanStack Table to recompute `getFilteredRowModel()` and `getFilteredSelectedRowModel()`. When connected to a Refine data provider, these state changes automatically serialize into query parameters for server-side filtering.

## Pagination Architecture

### Pagination Component Integration

The Table component renders pagination controls after the table body by passing the table instance to a dedicated Pagination component:

```tsx
<Pagination table={table} />

```

*Source:* [`packages/theme/src/table/index.tsx#L64-L66`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx#L64-L66)

### Navigation and Page Size Controls

The `Pagination` component in [`packages/theme/src/table/fields/pagination.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/pagination.tsx) reads current state from `table.getState().pagination` and exposes controls for:

- **Page size changes**: `table.setPageSize(size)`
- **Direct page navigation**: `table.setPageIndex(index)`
- **Relative navigation**: `table.nextPage()` and `table.previousPage()`
- **Boundary jumps**: First page via `setPageIndex(0)` and last page via `setPageIndex(table.getPageCount() - 1)`

The component also displays selection metadata using `table.getFilteredSelectedRowModel().rows.length` to indicate how many rows are selected across pages.

*Source:* [`packages/theme/src/table/fields/pagination.tsx#L27-L104`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/pagination.tsx#L27-L104)

### Hook-Based State Synchronization

The `useTable` hook maintains pagination state in `table.getState().pagination`. Method calls like `setPageIndex()` update this state, causing TanStack Table to recalculate `getPageCount()` and the visible row model. For server-side implementations, these changes propagate to the Refine query, updating parameters like `limit` and `offset` automatically.

## Complete Implementation Example

The following example demonstrates a fully configured Table with sorting, filtering, and pagination enabled:

```tsx
import { Table } from "@/ui/table";

<Table
  showHeader
  columns={[
    {
      id: "title",
      accessorKey: "title",
      header: "Title",
      enableSorting: true,
      filter: Table.Filter.Search,
    },
    {
      id: "status",
      accessorKey: "status",
      header: "Status",
      enableSorting: true,
      filter: Table.Filter.Dropdown,
    },
    {
      id: "created_at",
      accessorKey: "created_at",
      header: "Created At",
      filter: Table.Filter.DateRangePicker,
    },
  ]}
/>

```

In this configuration:

1. **Sorting** is enabled via `enableSorting: true`, which renders `SortAction` components in headers
2. **Filtering** is provided by assigning `Table.Filter.Search`, `Table.Filter.Dropdown`, or `Table.Filter.DateRangePicker` to the `filter` property
3. **Pagination** is automatically rendered below the table body using the internal `Pagination` component

The `Table` component wires these properties into TanStack Table's API through the `useTable` hook, creating a cohesive data grid experience.

## Summary

- **Sorting** relies on `column.toggleSorting()` and `column.getIsSorted()` managed by the `SortAction` component in header cells, with state handled by `@refinedev/react-table`.
- **Filtering** uses `column.setFilterValue()` through specialized filter field components (Search, Dropdown, DateRangePicker) that render conditionally based on column configuration.
- **Pagination** is implemented via the `Pagination` component calling `table.setPageIndex()`, `table.nextPage()`, and `table.setPageSize()` against the TanStack Table instance.
- All features integrate with Refine's data fetching layer through the `useTable` hook, automatically synchronizing UI state with server queries.

## Frequently Asked Questions

### How does refine-shadcn Table handle server-side sorting?

The Table component delegates server-side sorting to the `useTable` hook from `@refinedev/react-table`. When `column.toggleSorting()` is invoked, the hook updates the sorting state and triggers a Refine query refetch, serializing sort parameters into the request. The UI re-renders only after the server returns sorted data, ensuring the displayed rows match the backend state.

### What filter types are available in the Table component?

The component provides three built-in filter types via `Table.Filter`: **Search** (text input), **Dropdown** (multi-select), and **DateRangePicker** (calendar range). Each implements `column.setFilterValue()` with appropriate value formats—strings for search, arrays for dropdowns, and date tuples for range pickers. Custom filter components can also be passed to the `filter` column property.

### Can pagination be customized or disabled?

Pagination controls render automatically when the table instance includes pagination state, but you can customize the UI by replacing the internal `Pagination` component or modifying [`packages/theme/src/table/fields/pagination.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/fields/pagination.tsx). To disable pagination entirely, omit the pagination configuration from the `useTable` hook options, which prevents the `Pagination` component from rendering in [`packages/theme/src/table/index.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/table/index.tsx).

### How does the Table component connect to Refine data providers?

The connection happens through the `useTable` hook, which bridges TanStack Table state with Refine's data fetching layer. When sorting, filtering, or pagination state changes, `useTable` translates these into Refine query parameters (like `sorters`, `filters`, `pagination`) and calls the configured data provider. The returned data populates `table.getRowModel()`, rendering the updated rows while maintaining the Shadcn UI styling.