Skip to content
Domphy

Column Visibility & Ordering

Column visibility

Enable column visibility — users can show/hide columns. createDomphyTable handles the state loop automatically; just pass enableHiding: true:

import { createDomphyTable } from "@domphy/table/domphy"

const dTable = createDomphyTable({
  data: rows,
  columns,
  getCoreRowModel: getCoreRowModel(),
  enableHiding: true,
})

To persist visibility externally (e.g. localStorage), pass your own state and handler:

import { toState } from "@domphy/core"

const columnVisibility = toState<Record<string, boolean>>({})

const dTable = createDomphyTable({
  data: rows,
  columns,
  getCoreRowModel: getCoreRowModel(),
  state: { columnVisibility: columnVisibility.get() },
  onColumnVisibilityChange: (updater) => {
    columnVisibility.set(
      typeof updater === "function"
        ? updater(columnVisibility.get())
        : updater
    )
  },
  enableHiding: true,
})

Column visibility toggle UI

getAllLeafColumns(l) accepts the listener directly, so the list re-renders whenever visibility changes without a separate version(l) call:

const ColumnToggle = {
  div: [
    { h4: "Columns" },
    {
      div: (l) => dTable.getAllLeafColumns(l).map((col) => ({
        _key: col.id,
        label: [
          {
            input: null,
            type: "checkbox",
            checked: col.getIsVisible(),
            disabled: !col.getCanHide(),
            onChange: () => col.toggleVisibility(),
          },
          { span: String(col.columnDef.header ?? col.id) },
        ],
      })),
    },
  ],
}

col.getCanHide() returns false for columns with enableHiding: false — prevent critical columns from being hidden.

Pin certain columns as always visible

const columns = [
  columnHelper.accessor("id", {
    header: "ID",
    enableHiding: false,   // cannot be hidden
  }),
  columnHelper.accessor("name", {
    header: "Name",
  }),
  columnHelper.accessor("status", {
    header: "Status",
  }),
]

Column ordering

Reorder columns programmatically or via drag-and-drop:

import { toState } from "@domphy/core"

const columnOrder = toState<string[]>([])

const dTable = createDomphyTable({
  data: rows,
  columns,
  getCoreRowModel: getCoreRowModel(),
  state: { columnOrder: columnOrder.get() },
  onColumnOrderChange: (updater) => {
    columnOrder.set(
      typeof updater === "function"
        ? updater(columnOrder.get())
        : updater
    )
  },
})

// Move a column
function moveColumn(fromId: string, toId: string) {
  dTable.table.setColumnOrder((old) => {
    const order = old.length
      ? [...old]
      : dTable.getAllLeafColumns().map(c => c.id)

    const from = order.indexOf(fromId)
    const to = order.indexOf(toId)
    if (from === -1 || to === -1) return old

    const next = [...order]
    next.splice(from, 1)
    next.splice(to, 0, fromId)
    return next
  })
}

Column ordering with drag and drop

Combine with @domphy/dnd for drag-to-reorder headers:

import { dragDrop } from "@domphy/dnd"
import { toState } from "@domphy/core"

const headerOrder = toState<string[]>([])

const HeaderRow = {
  tr: (l) => {
    const headers = dTable.getHeaderGroups(l)[0]?.headers ?? []
    return headers.map((header) => ({
      th: String(header.column.columnDef.header ?? ""),
      _key: header.id,
      style: { cursor: "grab" },
    }))
  },
  $: [dragDrop(headerOrder, {
    group: "column-headers",
    onSort: (order) => dTable.table.setColumnOrder(order),
  })],
}

Persisting column state

Save column visibility and order to localStorage:

const STORAGE_KEY = "table-column-state"

interface ColumnState {
  visibility: Record<string, boolean>
  order: string[]
}

function loadColumnState(): ColumnState {
  try {
    return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "null") ?? { visibility: {}, order: [] }
  } catch {
    return { visibility: {}, order: [] }
  }
}

function saveColumnState(state: ColumnState) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
}

const saved = loadColumnState()
const columnVisibility = toState(saved.visibility)
const columnOrder = toState(saved.order)

// Persist on change
columnVisibility.addListener((v) => saveColumnState({ visibility: v, order: columnOrder.get() }))
columnOrder.addListener((o) => saveColumnState({ visibility: columnVisibility.get(), order: o }))

"Show all" / "Hide all" buttons

const ShowAllButton = {
  button: "Show all",
  onClick: () => dTable.table.resetColumnVisibility(),
}

const HideButton = (columnId: string) => ({
  button: "Hide",
  onClick: () => dTable.table.getColumn(columnId)?.toggleVisibility(false),
})

Visibility API

dTable = the DomphyTable returned by createDomphyTable. Methods marked with (l?) accept an optional listener for reactive reads.

MethodDescription
col.getIsVisible()true if column is visible
col.getCanHide()false if enableHiding: false
col.toggleVisibility(value?)Toggle or set visibility
dTable.getAllLeafColumns(l?)All leaf columns (visible or hidden)
dTable.getVisibleLeafColumns(l?)Visible leaf columns only
dTable.getIsAllColumnsVisible(l?)true if all columns visible
dTable.getIsSomeColumnsVisible(l?)true if some columns visible
dTable.table.toggleAllColumnsVisible()Toggle all columns
dTable.table.resetColumnVisibility()Reset to initial state
dTable.table.setColumnOrder(updater)Set column order
dTable.table.resetColumnOrder()Reset to initial order