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()
}
}