Select.table

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

name*
string

The name of the input. This will be the key in the result object.


data*
T[]

An array of objects representing the rows to select from. Each object's keys become columns in the table.


columns
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.


mode
'single' | 'multi'

Selection mode. Use single for radio button selection (returns one object) or multi for checkbox selection (returns an array). Defaults to multi.


min
number

Minimum number of items that must be selected. Only applies when mode is multi.


max
number

Maximum number of items that can be selected. Only applies when mode is multi.


helpText
string

Help text displayed below the table.


optional
boolean

Whether selection is optional. Inputs are required by default.


disabled
boolean

Whether the table selection is disabled.


validate
(value) => boolean | string

Custom 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:

  • multi mode (default): Returns an array of the selected row objects (T[])
  • single mode: Returns the selected row object (T), or undefined if optional is 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"],
});