Appica UIUI 1.0.0

Data Table

A data table recipe — Appica UI components wired to TanStack Table for sorting, filtering, selection, column visibility, and pagination.

A data table isn't a single component. It's a recipe: you keep your own column model and feature logic in TanStack Table — a headless, unstyled engine — and render its output with Appica UI components: Table, Checkbox, Thumbnail, Badge, Input, Dropdown Menu, and Scroll Area.

This page is one complete, copy-pasteable example rather than a packaged component — adapt the columns and data to your own shape.

Installation

TanStack Table is the only extra dependency. It ships its own types, so nothing else is needed.

pnpm add @tanstack/react-table

Example

A transactions table with row selection, a searchable global filter, sortable columns, a show/hide columns menu, per-row actions, and client pagination. Resize the frame down and the table scrolls horizontally instead of cramping.

How it works

TanStack Table owns the state and the row model; Appica UI owns the rendering. The bridge is flexRender, which turns each column's header/cell definition into React nodes that you drop straight into Table parts:

Each feature is a state hook plus the matching row-model helper passed to useReactTable:

  • Row selection — a select column whose header and cell render a Checkbox. The header checkbox wires checked to getIsAllPageRowsSelected() and indeterminate to getIsSomePageRowsSelected() for the mixed-state dash; mark the row highlighted when row.getIsSelected() to tint it.
  • SortinggetSortedRowModel() plus a SortableHeader button that calls column.toggleSorting() and swaps a ChevronsUpDown / ChevronUp / ChevronDown icon off column.getIsSorted().
  • Global filtergetFilteredRowModel() with globalFilterFn: 'includesString', driven by an Input whose startSlot holds a search icon.
  • Column visibility — a Dropdown Menu of DropdownMenuCheckboxItems, one per table.getAllColumns().filter((c) => c.getCanHide()), each toggling column.toggleVisibility().
  • PaginationgetPaginationRowModel() and an initialState.pagination.pageSize, with Previous / Next Buttons gated on getCanPreviousPage() / getCanNextPage().
  • Per-row actions — a Dropdown Menu in a trailing actions column. Mark it enableSorting: false and enableHiding: false so it stays put.

Rich cells

Because a cell returns arbitrary JSX, you compose the same components you'd use anywhere. The description cell pairs a Thumbnail (an icon-soft variant for category icons, an image for brand logos) with an emphasized label, and the category cell renders an outline Badge with a small color dot — keeping the badge neutral while the dot carries the category color:

Horizontal scroll

Wrap the table in a horizontal Scroll Area and keep cells from wrapping with whitespace-nowrap, so a wide table scrolls sideways on small screens instead of squeezing its columns:

Accessibility

  • The markup is a real <table>flexRender only fills the cells, so screen readers still announce rows, columns, and headers natively. See the Table notes.
  • Give every selection Checkbox an aria-label ("Select all rows", "Select row") — the checkbox has no visible text of its own.
  • Sort controls are real <button>s inside the <th>, so they're keyboard-focusable and operable with Enter/Space.
  • The per-row actions trigger needs an aria-label (e.g. "Open actions menu") since it's an icon-only button.
  • Row highlighted is a background cue only; selection state is conveyed by the checkbox, not by color alone.

© 2026 Appica UI. A free component library, crafted by the Appica team.