Table select
A table-based selection component for choosing from structured data. Supports single-select (radio buttons) or multi-select (checkboxes) modes, with the full context of your data visible in columns.
ctx.ui.select.table("name", config);Config
stringThe name of the input. This will be the key in the result object.
T[]An array of objects representing the rows to select from. Each object's keys become columns in the table.
string[]An array of keys from the row objects to define which columns to display and their order. If not provided, all keys from the first row are shown.
'single' | 'multi'Selection mode. Use single for radio button selection (returns one object) or multi for checkbox selection (returns an array). Defaults to multi.
numberMinimum number of items that must be selected. Only applies when mode is multi.
numberMaximum number of items that can be selected. Only applies when mode is multi.
stringHelp text displayed below the table.
booleanWhether selection is optional. Inputs are required by default.
booleanWhether the table selection is disabled.
(value) => boolean | stringCustom validation function. Return true if valid, or a string with an error message if invalid.
Return value
The return type depends on the mode setting:
multimode (default): Returns an array of the selected row objects (T[])singlemode: Returns the selected row object (T), orundefinedifoptionalis true and nothing is selected
The returned objects contain all the data from the original row, not just the displayed columns.
Usage
Table select is ideal when users need to see multiple data points before making a selection. Add it to a page to collect the user's choice.
const result = await ctx.ui.page({
title: "Select items",
content: [
ctx.ui.select.table("selectedUsers", {
data: [
{ id: "1", name: "Alice", role: "Admin", email: "alice@example.com" },
{ id: "2", name: "Bob", role: "Editor", email: "bob@example.com" },
{ id: "3", name: "Charlie", role: "Viewer", email: "charlie@example.com" },
],
columns: ["name", "role", "email"],
}),
],
});
// Multi mode (default) returns an array
console.log(result.selectedUsers); // [{ id: "2", name: "Bob", ... }, ...]Single selection mode
Use mode: "single" when only one item should be selected:
const result = await ctx.ui.page({
title: "Select item",
content: [
ctx.ui.select.table("selectedUser", {
data: users,
columns: ["name", "role"],
mode: "single",
}),
],
});
// Single mode returns one object
console.log(result.selectedUser); // { id: "1", name: "Alice", ... }ERP example: Selecting lots for stock adjustment
A common warehouse operation is adjusting stock for a specific lot. Table select makes it easy to display lot details and let the user pick the right one:
const lots = await models.lot.findMany({
where: { inventoryItemId: inventoryItem.id },
});
const lotSelection = await ctx.ui.page({
title: `Select lot for ${inventoryItem.name}`,
description: "Choose which lot to adjust",
content: [
ctx.ui.select.table("lot", {
data: lots.map((lot) => ({
id: lot.id,
lotNumber: lot.lotNumber,
onHand: lot.onHand,
available: lot.available,
expiryDate: lot.expiryDate
? new Date(lot.expiryDate).toLocaleDateString("en-GB")
: "-",
location: lot.locationName,
})),
columns: ["lotNumber", "onHand", "available", "expiryDate", "location"],
mode: "single",
}),
],
});
// The full object is returned, including the id for database operations
const selectedLot = lotSelection.lot;
await models.lot.update({
where: { id: selectedLot.id },
values: { onHand: newQuantity },
});Controlling displayed columns
You can include data in the row objects that isn't displayed in the table. This is useful for IDs or other metadata you need after selection:
ctx.ui.select.table("products", {
data: products.map((p) => ({
// Hidden from display but available in result
id: p.id,
inventoryItemId: p.inventoryItemId,
// Visible columns
sku: p.sku,
name: p.name,
quantity: p.quantity,
})),
// Only these columns appear in the table
columns: ["sku", "name", "quantity"],
});