Search documentation

Search for a page or heading...

tablecn
0

Layout & persistence

Let users hide, reorder, pin, resize and color columns, and keep their layout across visits.

The layout is how a user arranges the columns: which are shown, in what order, pinned where, how wide, in what color. Each user keeps their own, saved in their browser.

It's separate from the URL state on purpose: a shared link shows the same rows to everyone, but each person keeps their own columns.

Save the layout

Give the table a storageKey. The layout is then saved to localStorage on every change, and restored on the next visit:

components/orders-table.tsx
const table = useDataTable({
  data,
  columns,
  getRowId: (order) => order.id,
  storageKey: "orders-table", 
})

Use one key per table. Without a storageKey, the layout resets when the table unmounts. Tables using the same key stay in sync, in this tab and in others.

Prop

Type

What users can change

To…Users…See
Reorderdrag a header, or the grip in the Columns menuMoving columns
Resizedrag the edge of a headerResizing
Fit a column to its contentdouble-click the edge of a header, or pick Fit to content in the column's menuFit to content
Fit every columnpick Fit all columns in the Columns menuFit to content
Pin to the start or endpick Pin to start or Pin to end in the column's menuPinning
Show or hide a columnpick Hide column in the column's menu, or tick its checkbox in the Columns menuHiding columns
Color a columnpick Color in the column's menuColumn colors
Go back to your defaultspick Reset layout in the Columns menuReset from code

Each page covers how users do it, the options to limit or turn it off, and how to do it from code. For all the switches in one place, see What users can do.

The column menu

Every header has a ⋯ button, unless the column has nothing to sort, pin, fit or hide, like the selection column. It shows when you hover the header or tab to it, and stays visible on touch screens. Right-clicking a header opens the same menu. It holds everything you can do to that one column:

ItemDoes
Ascending, Descending, Clear sortsorts by this column only (Shift+click the header to sort by several)
Pin to start, Pin to end, Unpinsee Pinning
Fit to contentsee Fit to content
Colorsee Column colors
Hide columnsee Hiding columns

Items a column doesn't allow are left out (sorting, hiding) or disabled (pinning, fitting). The same pin, fit and color items are in each row's ⋯ menu in the Columns menu, which also lists hidden columns.

The header itself shows a grab cursor when it can be dragged, and a thin line on its end edge when you hover where it resizes.

Your column definitions decide what's allowed, and what the layout starts from: see Columns. A column that can't be hidden, pinned or moved, like the selection column, isn't listed in the Columns menu.

When your columns change

You can ship new columns without breaking anyone's saved layout. A saved layout is always matched against your current columns:

  • New columns start from their defaults, at the end of the order.
  • Removed columns are dropped from the saved layout.
  • Locked options (enableHiding: false, enablePinning: false) follow your definitions, whatever was saved.
  • Anything unreadable (hand-edited, a wrong shape) falls back to the defaults.

If old layouts would be wrong rather than just outdated, for example after renaming column ids, bump layoutVersion. Layouts saved under another version are ignored.

Reset from code

Reset layout calls table.options.meta.resetLayout(). It goes back to your column definitions and deletes the saved layout. Call it from your own button the same way:

Your own reset button
<Button variant="ghost" onClick={() => table.options.meta?.resetLayout()}>
  Reset layout
</Button>

Use resetLayout, not TanStack's reset methods

table.resetColumnOrder(), resetColumnVisibility() and the like go back to the columns of the first render, and they don't delete the saved layout. meta.resetLayout() always uses your current definitions.

Good to know

  • Server rendering. The server has no localStorage, so the first render uses your column definitions, and the saved layout applies right after. If that brief switch bothers you, render the table on the client only.
  • Storage can fail. If the browser refuses to store (full, private mode), changes still apply until the page is closed.
  • Right-to-left pages. Pass dir: "rtl" to useDataTable. Resizing follows the pointer, and pinned columns and their shadows flip with the page. See Right-to-left.
  • Your own table setup. useTableLayout({ columns, storageKey, version }) is the hook useDataTable uses. It returns state, handlers and reset() to spread into your own TanStack table. Add columnColorFeature to its features for colors.