Column Visibility

Compose DataTable column visibility with a dedicated hook and menu components.

Overview

Column visibility is composed from two pieces:

  • useDataTableColumnVisibility owns the visible column ids, optional URL sync, and the derived TanStack columnVisibility map
  • Menu components render the trigger and checkbox items

This keeps DataTable focused on rendering while the consumer stays free to choose the surrounding UI.

Locked Columns

Columns marked with enableHiding: false stay visible and are omitted from the hook's menu items.

const columns: ColumnDef<Employee>[] = [
  {
    id: "name",
    header: "Name",
    accessorKey: "name",
    enableHiding: false,
  },
  {
    id: "email",
    header: "Email",
    accessorKey: "email",
  },
];

Defaults and URL State

Pass defaultValue as the list of hideable column ids that should start visible.

const columnView = useDataTableColumnVisibility({
  columns,
  defaultValue: ["role", "department", "status"],
  paramName: "columns",
});

The hook stores visible hideable column ids and derives the columnVisibility object that DataTable expects.

Labels

By default the hook uses:

  • header when it is a string
  • otherwise the resolved column id

If your headers are custom JSX or functions, pass getColumnLabel:

const columnView = useDataTableColumnVisibility({
  columns,
  getColumnLabel: (column, columnId) =>
    columnId === "assignee" ? "Assigned To" : columnId,
});

API Reference

Hook Options

OptionTypeDefaultDescription
columnsColumnDef<TData>[]RequiredDataTable column definitions
valuestring[]-Controlled list of visible hideable column ids
defaultValuestring[]All hideable column idsInitial visible hideable column ids
onValueChange(value: string[]) => void-Called when the visible id list changes
getColumnLabel(column, columnId) => string-Override the label for a column
paramNamestring-URL parameter used to persist the visible id list
paramClearOnDefaultbooleantrueRemove the query param when equal to the default visible ids
paramThrottlenumber-Throttle URL updates in milliseconds
paramDebouncenumber-Debounce URL updates in milliseconds
onUrlValueChange(value: string[] | null) => void-Called when the URL-backed visible id list changes

Hook Return Value

FieldTypeDescription
itemsDataTableColumnVisibilityItem[]Menu-safe hideable columns with derived ids, labels, and visible state
resolvedItemsDataTableColumnVisibilityResolvedItem<TData>[]Row-typed hideable columns including the original column definition
valuestring[]Current visible hideable column ids
columnVisibilityVisibilityStateDerived state to pass to DataTable
onValueChange(value: string[]) => voidControlled setter for visible ids
onColumnVisibilityChangeOnChangeFn<VisibilityState>Adapter for DataTable updates
isColumnVisible(columnId: string) => booleanCheck whether a hideable column is visible
setColumnVisible(columnId: string, visible: boolean) => voidSet one hideable column visible or hidden
toggleColumn(columnId: string) => voidToggle one hideable column
showAll() => voidShow every hideable column
hideAll() => voidHide every hideable column
allVisiblebooleanWhether every hideable column is visible
someVisiblebooleanWhether at least one hideable column is visible

items is intentionally non-generic so reusable menus and view switchers can accept column visibility state from multiple table row types. If you need the original ColumnDef<TData>, use resolvedItems.

On this page