Opinionated data grid with sort, selection, filters, and pagination.
Introduction
DataTable is an opinionated grid for columns and rows: sorting, selection, filters, search, empty and loading states, and pagination wiring. It composes Table for layout, sticky header, and column alignment. size, variant, full, striped, hoverable, and rounded are forwarded to Table.
When to use: Interactive datasets where the app owns fetch and DataTable owns chrome. Use Table for static markup you compose yourself.
Compared to similar components:
- Table — Presentational primitives (
table/thead/tbody). No sort, selection, or pager. - Pagination — Page controls. DataTable renders Pagination when
pageis set with a total, or when client paging slicesrows. - List — A vertical index of items. Use List for navigation rows; use DataTable when values line up across columns and need grid chrome.
Use the framework selector in the site header to switch between React and Vue.
Install @tanstack/vue-table next to @bridge-ui/vue. The public API stays columns / rows / sorting — the table engine is not exported.
Install @tanstack/react-table next to @bridge-ui/react. The public API stays columns / rows / sorting — the table engine is not exported.
Import
import { DataTable } from "@bridge-ui/vue/Components/DataTable";import { DataTable } from "@bridge-ui/react/Components/DataTable";Basic usage
Pass columns and rows. Set sortable on a column and bind sorting so header clicks cycle unsorted → ascending → descending. Cell text defaults to row[id] (or accessor) when no renderer is set.
Variants
Chrome matches Table: plain (default), ghost, or bordered. Built-in Pagination follows the pairing unless slots.pagination overrides it.
| DataTable variant | Pagination variant |
|---|---|
| plain | text |
| ghost | ghost |
| bordered | outlined |
striped and hoverable are independent of variant. full={false} sizes columns to content; the bordered wrapper, per-page Select, and pagination follow that width. stickyHeader pins header cells to the page; "boxed" pins them inside the wrapper — set a max height on classes.wrapper.
Pagination
Built-in Pagination when page is set with pageCount or totalCount (server fetch stays in the app). page + perPage without those totals slices rows locally. perPage also renders a Select (perPageOptions, default 10 / 25 / 50 / 100).
When page and a total are set, DataTable does not sort or filter rows locally — bind sorting / filters / page and fetch the current page in the app. With perPage, the per-page Select and pager sit on opposite ends when they fit on one row of the bar; they stack and center when they overflow. Without perPage, the pager aligns to the end. Replace the controls with slots.pagination and slots.perPage.
Selection
Pass selection (always string[]) and getRowId. selectionMode="multiple" (default) uses checkboxes and select-all. selectionMode="single" uses radios and keeps at most one id.
Filters
Set filters on a column to show a funnel in that header. The panel uses checkboxes (filterMultiple, default) or radios (filterMultiple={false}), and Select all items for multiple filters. Nested children render as a group. OK commits; Reset clears the draft (commit on OK); closing without OK discards it.
Set searchable on a column to add a text field in that same overlay. Queries live in columnSearch (column id → string) and filter rows — they do not filter the option list. Hidden columns skip their column-search queries.
Column filters open in a FieldOverlay. Default filterOverlay is auto (menu on desktop, drawer on mobile). Pass menu, modal, or drawer to pin a shell.
Search and toolbar
Pass search and/or onSearchChange to show a search field in the toolbar. Client-side tables filter visible columns; server-paged tables emit the query only.
slots.toolbar is the leading region. slots.toolbarActions sits in the end cluster beside Columns and Search. The toolbar shows when either slot is set, or when Columns / Search are enabled.
Sticky columns
sticky="start" or sticky="end" pins a column while the table scrolls horizontally. Selection and expand chrome pin to start when any data column uses sticky="start". Set width on pinned columns so offsets stay stable; pinned columns without a parseable width fall back to 120px. Keep those widths modest so other columns stay visible on small screens. Columns need enough width (or a constrained wrapper) so the table overflows and a horizontal scrollport appears.
Column classes
classes.header and classes.cell merge onto that column’s th / td (body and footer). Use width utilities for breakpoint-based sizing. Do not set width on the same column when the class should control width — inline width wins.
Column visibility
Pass hiddenColumns and/or onHiddenColumnsChange to show a Columns icon in the toolbar. hideable={false} keeps a column out of the toggle (or disabled). At least one column stays visible.
The panel opens in a FieldOverlay. Default columnsOverlay is auto (menu on desktop, drawer on mobile). Pass menu, modal, or drawer to pin a shell.
When unset, columnsShowFooter is true for modal / drawer (false for menu). Reset restores hideable columns; OK commits and closes. Closing without OK discards the draft.
Expand
Controlled expanded row ids. slots.expanded renders in a spanning row under the data row.
Empty, loading, and footer
Empty rows show a default empty state; slots.empty replaces it. loading keeps the table visible. loadingVariant="overlay" (default) dims the table with a spin; loadingVariant="bar" dims the table and draws a progress line under the header. slots.loading replaces the indicator. slots.footer renders below the table, above pagination.
Item slots
cell on a column is for portable renderers. An item slot wins: React slots.item.{id}, Vue #item.{id}.
Summary
Set summary on a column to render a footer row over the current (filtered) rows. Chrome cells stay empty. Set ellipsis on a column to truncate overflowing cell text; the tooltip uses the column accessor.
Related components
Table, Pagination, List, Select, Menu
Accessibility
- DataTable keeps a real table with thead / tbody / optional tfoot.
- Sortable headers set aria-sort (
none/ascending/descending). - Selection checkboxes and radios are named Select row and Select all rows.
- Filter, Columns, and Search controls are named buttons/fields. Expand uses Expand row.
- loading sets aria-busy on the table.
Anatomy
<div> <!-- DataTable root -->
<div> <!-- toolbar: slots.toolbar, Columns, slots.toolbarActions, Search -->
<div> <!-- frame: hugs table when full is false -->
<div> <!-- table + loading overlay -->
<table> <!-- Table -->
<thead> <!-- header row, optional bar loading -->
<tbody> <!-- data rows, expand rows, empty state -->
<tfoot> <!-- summary -->
<div> <!-- slots.footer -->
<div> <!-- per-page Select + Pagination -->
</div>Use classes for root, toolbar, wrapper, table, header, body, row, head, cell, empty, loading, footer, search, pagination, and perPage. Each column also accepts classes.header and classes.cell.
API
Bindings
| Prop / event | Type | Description |
|---|---|---|
| v-model:column-search | DataTableColumnSearch | Column id → text query. |
| v-model:expanded | string[] | Expanded row ids. |
| v-model:filters | DataTableFilters | Column id → selected option values. |
| v-model:hidden-columns | string[] | Hidden column ids. |
| v-model:page | number | 1-based page. |
| v-model:per-page | number | Page size. |
| v-model:search | string | Toolbar search query. |
| v-model:selection | string[] | Selected row ids. |
| v-model:sorting | DataTableSorting | Controlled sort, or null when unsorted. |
Bindings
| Prop | Type | Description |
|---|---|---|
| columnSearch | DataTableColumnSearch | Column id → text query. |
| expanded | string[] | Expanded row ids. |
| filters | DataTableFilters | Column id → selected option values. |
| hiddenColumns | string[] | Hidden column ids. |
| onColumnSearchChange | (search) => void | Called when column search changes. |
| onExpandedChange | (ids: string[]) => void | Called when expanded ids change. |
| onFiltersChange | (filters) => void | Called when filters change. |
| onHiddenColumnsChange | (ids: string[]) => void | Called when visibility changes. |
| onPageChange | (page: number) => void | Called when the page changes. |
| onPerPageChange | (perPage: number) => void | Called when page size changes (also resets to page 1). |
| onSearchChange | (query: string) => void | Called when toolbar search changes. |
| onSelectionChange | (ids: string[]) => void | Called when selection changes. |
| onSortingChange | (sorting) => void | Called when sort changes. |
| page | number | 1-based page. |
| perPage | number | Page size. |
| search | string | Toolbar search query. |
| selection | string[] | Selected row ids. |
| sorting | DataTableSorting | Controlled sort, or null when unsorted. |
DataTable
| Prop | Type | Default | Description |
|---|---|---|---|
| classes | DataTableClasses | — | Classes for root, toolbar, table wrapper, and descendant parts. |
| columns | DataTableColumn[] | [] | Column definitions. |
| columnsOverlay | FieldOverlayMode | "auto" | Overlay shell for the Columns panel: menu, modal, drawer, or auto. |
| columnsShowFooter | boolean | — | Reset / OK on the Columns overlay. Unset: true for modal / drawer, false for menu. Draft until OK. |
| customProps | DataTableCustomProps | — | Extra props for table parts, selection controls, Pagination, per-page Select, search field, and progress. |
| filterOverlay | FieldOverlayMode | "auto" | Overlay shell for column filter panels: menu, modal, drawer, or auto. |
| full | boolean | true | Stretch the table to at least the wrapper width. When false, the wrapper hugs column content, and pagination / per-page follow that width. |
| getRowId | (row: T) => string | — | Stable row id. Required for reliable selection and expand. |
| hoverable | boolean | false | Row hover styles on the body. |
| loading | boolean | false | Show the loading region (aria-busy on the table). |
| loadingVariant | "overlay" \| "bar" | "overlay" | Overlay spin, or a progress line under the header. Both dim the table. |
| pageCount | number | — | Total pages for built-in Pagination. Use this or totalCount. |
| perPageOptions | number[] | [10, 25, 50, 100] | Options for the built-in per-page Select. |
| rounded | TableRounded | "lg" | Corner radius of the wrapper, header, and footer. |
| rows | T[] | [] | Current page of data, or the full set when not server-paging. |
| selectionMode | "single" \| "multiple" | "multiple" | Radios or checkboxes. |
| size | TableSize | "md" | Cell padding and type scale: sm, md, lg. |
| slots | DataTableSlots | — | empty, expanded, footer, item, loading, pagination, perPage, search, toolbar, toolbarActions. |
| stickyHeader | boolean \| 'boxed' | false | Pin headers to the page, or inside the wrapper when "boxed". |
| striped | boolean | false | Alternating body row surfaces. |
| totalCount | number | — | Total item count; derives page count with perPage. pageCount wins if both are set. |
| variant | TableVariant | "plain" | Chrome forwarded to Table. Built-in Pagination follows the matching variant. |
Column
| Field | Type | Default | Description |
|---|---|---|---|
| accessor | (row: T) => unknown | row[id] | Value used for client-side sorting, filtering, search, and ellipsis tooltips. |
| align | TableAlign | "start" | Text alignment for the header and cells. |
| cell | (row: T) => ReactNode | accessor / row[id] | Portable cell renderer. Item slots win. |
| classes | DataTableColumnClasses | — | Extra classes for this column: header on the th, cell on body and footer td. |
| ellipsis | boolean | false | Truncate overflowing cell text and show the full value in a tooltip. |
| filterMultiple | boolean | true | When false, the filter overlay allows only one value. |
| filters | DataTableFilterOption[] | — | Filter overlay options. Presence enables the header funnel. |
| header | ReactNode | — | Header content. |
| hideable | boolean | true | When false, the column cannot be toggled in the Columns overlay. |
| id | string | — | Stable column id (also the default accessor key). |
| searchable | boolean | false | Adds a text field in that header’s filter overlay. |
| sortable | boolean | — | Whether the column can be sorted. |
| sticky | "start" \| "end" | — | Pin the column while the table scrolls horizontally. |
| summary | (rows: T[]) => ReactNode | — | Footer cell for a summary row over the current (filtered) rows. |
| width | number \| string | — | Column width (px number or CSS length). Wins over class width when both are set. |