react-datatable
Examples

Boolean toggle cell

Use this when a row owns one simple true-or-false state such as paying, enabled, or verified.

Interactive cell

Live preview

Paying customer

Build the React component

This is one of the simplest interactive cells possible: render the switch and make sure it does not fight the row click behavior around it.

import { Switch } from "@/components/ui/switch"

export function BooleanToggleCell({
  value,
  onChange,
}: {
  value: boolean
  onChange: (value: boolean) => void
}) {
  return (
    <div className="flex justify-center" onClick={(event) => event.stopPropagation()}>
      <Switch checked={value} onCheckedChange={onChange} />
    </div>
  )
}

Wire it into a column definition

The field stays a boolean in the column contract. The cell only turns that boolean into a compact switch UI.

Even with a custom cell, the column still needs an accessor. In this example that is accessorKey="paying". That gives the table a stable field for sorting, filtering, and saved state, while the cell can still read extra data from row.original.
{
  id: "paying",
  header: "Paying",
  accessorKey: "paying",
  width: 100,
  enableSorting: false,
  enableFiltering: false,
  cell: ({ row }) => (
    <BooleanToggleCell
      value={row.original.paying}
      onChange={(paying) => updateRow(row.original.id, { paying })}
    />
  ),
}

Render it in a small table

The demo table shows the switch in context so you can verify that the row still feels like a table row rather than a loose form.

function PayingExampleTable() {
  const [rows, setRows] = useState(seedRows)

  const updateRow = (id: string, patch: Partial<CustomerRow>) => {
    setRows((current) => current.map((row) => (row.id === id ? { ...row, ...patch } : row)))
  }

  return <Datatable tableKey="custom-cells" data={rows} columns={columns(updateRow)} getRowId={(row) => row.id} toolbar={false} />
}

Mini table demo

Loading table preferences...

Persist changes with optimistic update

For backend writes, optimistic boolean flips are straightforward: update the row, send the mutation, then refetch to confirm authority.

async function updatePaying(id: string, paying: boolean) {
  setRows((current) => current.map((row) => (row.id === id ? { ...row, paying } : row)))

  try {
    await api.customers.updateBillingState({ id, paying })
    await refetchCustomers()
  } catch {
    await refetchCustomers()
  }
}

On this page