Wire CRUD actions to the table via provider callbacks
Actions API
Provide actions per tableType via getTableActions (passed to DataTable). These connect the table to your data layer. In Next.js you can pass Server Actions so that list, create, update, delete, and bulk operations run on the server. See Server-side & Server Actions for a full example.
Shape
getTableActions: (tableType: string) => ({
list: async (params) => {
// params: { filters, advancedFilters, limit, orderBy, page (1-based), search }
return {
data: [],
meta: { pageCount: 1, totalCount: 0 },
};
},
aggregate: async (params) => {
// params: { filters, advancedFilters, search, calculations, locale }
return {
results: {
price: { raw: 820.64, label: "820,64" },
},
meta: { totalCount: 50 },
};
},
create: async (data) => ({ success: true, data }),
update: async (id, data) => ({ success: true, data }),
delete: async (id) => ({ success: true }),
duplicate: async (id) => ({ success: true }),
bulkDelete: async (ids) => ({ success: true }),
bulkCopy: async (ids) => ({ success: true, data?: string }),
bulkUpdate: async (ids, updateData) => ({ success: true }),
})list response
The list method must return:
{ data: T[]; meta?: { pageCount?: number; totalCount?: number } }aggregate response (optional)
aggregate is used by footer calculations to compute values on the full filtered dataset (not only the current page).
{
results: Record<
string,
{
raw: number | string | null;
label: string;
}
>;
meta?: { totalCount?: number };
}Notes:
calculationsin params is a map:columnId -> CalculationType.- If
aggregateis not provided, the table falls back to paginatedlistcalls. - You can return custom labels from your API (
label) while keeping machine-readable values inraw.
Bulk actions
DataTable will use these by default if you don't provide explicit callbacks:
onBulkEdit→updateonBulkDelete→deleteonBulkCopy→duplicateonBulkExport→ internal CSV export of selected rows (client-side)
Inline edit dependency
Inline cell editing depends on the provider update(id, data) action.
- No extra API route is used.
- When
inlineEditis enabled on the table or columns, each cell commit callsupdate(id, { [field]: value }). - If
updateis not configured, inline edit shows an error and does not commit.
Recommended bulk callback contract
To avoid ambiguous branches, return an explicit result object from your bulk callbacks:
type BulkActionResult = {
success: boolean;
closeMenu: boolean;
clearSelection: boolean;
message?: string;
};Example:
onBulkDelete: async (rows) => {
const ids = rows.map((row) => String((row.original as { id: string }).id));
const response = await deleteManyProducts(ids);
return {
success: response.success,
closeMenu: response.success,
clearSelection: response.success,
message: response.success
? `Deleted ${ids.length} products`
: response.error ?? "Delete failed",
};
};See also: