Responsive React table — automatic mobile card layout
▶ See it before you install: the live mobile demo — the same table flipped between desktop rows and phone cards with one toggle.
Most React data tables answer the phone problem with a horizontal scrollbar.
AdaptTable answers it with a different layout: below the mobile breakpoint,
every row renders as a card — same columns, same filters, same search,
same selection, same URL state — and the switch happens by itself. There is
no second component to build, no isMobile plumbing, and nothing to
configure to get the default behavior.
<DataTable data={rows} columns={columns} rowKey={(r) => r.id} />That is already responsive. On a desktop viewport it renders the full table; on a phone it renders the card list. Everything below is tuning.
What changes on mobile — and what deliberately doesn’t
Section titled “What changes on mobile — and what deliberately doesn’t”| Surface | Desktop | Mobile |
|---|---|---|
| Rows | <table> rows | Cards — one per row, labels from the column headers |
| Sorting | Clickable column headers | A sort-by <select> (options via sortByOptions) |
| Pagination | Pager (or infinite) | paginationMode="auto" resolves to infinite scroll |
| Row actions | Trailing icon buttons | Card buttons |
| Long lists | Row virtualization (virtualize) | Card virtualization through the same prop |
| Filters, chips, search, selection, bulk bar, saved views, URL state | identical | identical |
The second half of that table is the point: behavior that took real work to get right — declarative filters, select-across-pages, shareable URL state — does not fork into a second code path on phones. One table, one state, two layouts.
Tuning the cards
Section titled “Tuning the cards”mobileLabel(per column) — the label a card shows for that field; falls back to the column’s stringheader.hideOnMobile(per column) — drop a column from cards entirely.mobileIdentityColumns(default3) — how many leading desktop-visible columns the cards always keep.sortByOptions— the options offered by the mobile sort-by select.forceMobile— pin either layout regardless of viewport: cards inside a desktop dashboard panel, or the full table in a tablet kiosk. The mobile demo uses exactly this prop for its toggle.rowClassNameapplies to desktop rows and mobile cards alike, and the class-hook /data-adapttable-partsurface names the card regions (cardDetail,group-card,summaryCard) for styling.
It composes with everything else
Section titled “It composes with everything else”- Grouping renders group header blocks between cards, with the same collapse behavior as desktop — see row grouping.
- Virtualization windows the card list the same way it windows rows:
one
virtualizeprop, measured in a real browser across every adapter — see virtualization. - RTL flips the cards along with everything else — see i18n & RTL.
- Every adapter ships it: Mantine, MUI, Chakra, Ant Design, Radix, Base UI, shadcn/ui and unstyled all render the card layout natively — the comparison table tracks it as a built-in across the board.
When you still want the table on phones
Section titled “When you still want the table on phones”Set forceMobile={false} and the desktop layout renders everywhere —
sticky header, pinned columns and horizontal scrolling included. The cards
are the default because thumb-reach beats pinch-zoom for row-by-row work,
but the choice stays yours per table.
Related: Getting started · API reference · Live mobile demo
