# Example: Column filters

One filterable column per type — text, number, enum, date, and boolean — reaching each operator family through the built-in funnel menu, with the enum column declaring no options so its checklist loads distinct values from the rows instead.

Source: https://pretable.ai/examples/column-filters.md

```tsx ColumnFiltersGrid.tsx
"use client";

import { useState, type ComponentProps } from "react";

import { PretableSurface } from "@pretable/react";

import { columns } from "./columns";
import { type Order, orders } from "./data";

const VIEWPORT_HEIGHT = 320;

export function ColumnFiltersGrid() {
  // The query is controlled and seeded with an initial filter so the active
  // filters can be echoed below the grid. Omit both query props to let rows
  // mode own filtering uncontrolled instead — nothing here requires control.
  //
  // Typed via `ComponentProps`, not `PretableQueryFor<typeof columns>`: these
  // plain PretableColumn<Order>[] carry no `accessor` field, which
  // `PretableQueryFor` requires — applied directly it collapses every filter
  // to `never`. Pulling the type through the prop instead picks up
  // `<PretableSurface>`'s own fallback for accessor-less columns.
  const [query, setQuery] = useState<
    NonNullable<ComponentProps<typeof PretableSurface<Order>>["query"]>
  >({
    filters: [{ columnId: "status", operator: "isAnyOf", value: ["open"] }],
    sort: [],
    rowGroups: [],
  });

  return (
    <div>
      <p style={{ margin: "0 0 8px", fontSize: 13 }}>
        Hover a header for its funnel — on a touch device it is drawn all the
        time, since there is no hover to reveal it with. <strong>Status</strong>{" "}
        declares no <code>options</code>, so its checklist loads distinct values
        from the rows. <strong>Total</strong>&rsquo;s <code>between</code> waits
        for both bounds before it filters anything.
      </p>
      <PretableSurface<Order>
        ariaLabel="Orders"
        columns={columns}
        getRowId={(row) => row.id}
        onQueryChange={setQuery}
        query={query}
        rows={orders}
        viewportHeight={VIEWPORT_HEIGHT}
      />
      <p style={{ margin: "8px 0 0", fontSize: 13 }}>
        Active filters:{" "}
        <code>
          {query.filters.length > 0
            ? query.filters
                .map((filter) => `${filter.columnId} ${filter.operator}`)
                .join(" · ")
            : "(none)"}
        </code>
      </p>
    </div>
  );
}
```

```ts columns.ts
import type { PretableColumn } from "@pretable/react";

import type { Order } from "./data";

export const columns: PretableColumn<Order>[] = [
  { id: "customer", header: "Customer", widthPx: 110 },
  { id: "total", header: "Total", type: "number", widthPx: 75 },
  // No `options` — the checklist loads its distinct values from the rows.
  { id: "status", header: "Status", type: "enum", widthPx: 95 },
  { id: "placedAt", header: "Placed", type: "date", widthPx: 100 },
  { id: "expedited", header: "Expedited", type: "boolean", widthPx: 115 },
];
```

```ts data.ts
export interface Order {
  id: string;
  customer: string;
  total: number;
  status: "open" | "shipped" | "cancelled";
  placedAt: string;
  expedited: boolean;
}

export const orders: Order[] = [
  {
    id: "o1",
    customer: "Ada Lovelace",
    total: 128,
    status: "open",
    placedAt: "2026-08-01",
    expedited: true,
  },
  {
    id: "o2",
    customer: "Grace Hopper",
    total: 412,
    status: "shipped",
    placedAt: "2026-08-03",
    expedited: false,
  },
  {
    id: "o3",
    customer: "Linus Torvalds",
    total: 76,
    status: "cancelled",
    placedAt: "2026-08-04",
    expedited: true,
  },
  {
    id: "o4",
    customer: "Margaret Hamilton",
    total: 205,
    status: "open",
    placedAt: "2026-08-06",
    expedited: false,
  },
  {
    id: "o5",
    customer: "Alan Turing",
    total: 340,
    status: "shipped",
    placedAt: "2026-08-07",
    expedited: true,
  },
  {
    id: "o6",
    customer: "Katherine Johnson",
    total: 58,
    status: "open",
    placedAt: "2026-08-08",
    expedited: true,
  },
  {
    id: "o7",
    customer: "Dennis Ritchie",
    total: 289,
    status: "shipped",
    placedAt: "2026-08-10",
    expedited: false,
  },
];
```
