Capítulo 34 de 54
columnVisibilityFeature is a simple { [columnId]: boolean } map (absent or true = shown, false = hidden) — the one easy-to-miss requirement is that every render/count API has a "visible" variant, and using the plain variant silently ignores hidden columns.
columnVisibility: Record<string, boolean>. A column is hidden only if explicitly false; missing from the map or true means shown.atoms: { columnVisibility: atom } (v9-recommended, e.g. to persist user column preferences) or classic state.columnVisibility + onColumnVisibilityChange; or just initialState.columnVisibility if you never need it outside the table. Don't set both initialState.columnVisibility and state.columnVisibility — state wins and initialState is ignored.enableHiding: false on a column def locks it visible (a required "id"/"actions" column, say) — column.getCanHide() reports false for it.column.getCanHide(), column.getIsVisible(), column.toggleVisibility(), column.getToggleVisibilityHandler() (bind directly to a checkbox's onChange).column.columnDef.header directly there — it's a render template (string/JSX/function needing full header context), and a hidden column may not have an active header context to render it with. Use a stable label instead: a columnId → label map, typed column.columnDef.meta?.label, or fall back to column.id.table.getAllLeafColumns()/row.getAllCells() do not account for visibility — use table.getVisibleLeafColumns()/row.getVisibleCells() instead when rendering the actual table. Header Group APIs (table.getHeaderGroups()) already account for visibility automatically.const features = tableFeatures({ columnVisibilityFeature })
const table = useTable({ features, columns, data, initialState: { columnVisibility: { age: false } } })
// visibility toggle menu
{table.getAllColumns().map((col) => (
<label key={col.id}>
<input type="checkbox" checked={col.getIsVisible()} disabled={!col.getCanHide()}
onChange={col.getToggleVisibilityHandler()} />
{columnLabels[col.id] ?? col.id}
</label>
))}
// rendering: always the "visible" variant
{row.getVisibleCells().map((cell) => /* ... */ null)}
columnDef.header), and the visibility-aware render call.getVisible* variant — getAllLeafColumns()/getAllCells() ignore visibility entirely, and this is the single most common bug with this feature.column.columnDef.header as a label for a visibility-toggle UI — use a dedicated label source instead.enableHiding: false is the right way to lock a structural column (checkboxes, row actions) visible.meta, a good home for a stable column label.