Column renderers
Format built-in values or render a custom server or interactive cell.
Column definitions control table cells and are reused by drawer/detail field rendering. Use a built-in format when it fits; reach for render only when the value needs application-specific presentation.
Use built-in formatting
columns: [
"email",
{ field: "status", format: { kind: "badge", tones: { active: "ok", blocked: "danger" } } },
{ field: "balance", label: "Balance", format: { kind: "money", currency: "USD" }, align: "right" },
]Built-in badge, money, and number formats stay consistent across the admin and preserve the field's normal sort/export behavior.
Render a custom server cell
columns: [
{
field: "customerId",
label: "Customer",
render: async (order, ctx) => {
const customer = await findCustomer(ctx.db, order.customerId);
return <a href={`/admin/customers/${customer.id}`}>{customer.email}</a>;
},
},
]render runs on the server with the row and request context. The function and database client never cross the browser boundary. Return only UI intended for the operator; the row still must be protected by resource access and scope.
For foreign keys, prefer a declarative reference when the target is another registered resource. It provides a consistent link and label lookup without a per-cell query.
Add an interactive client cell
A server renderer may return a client component:
render: (row) => <CopyId value={String(row.id)} />Keep the client props small and serializable. The client component may manage interaction, but it should call an authorized server route for mutations rather than receiving server services as props.
Joined or computed values
Omit field when the cell is derived entirely from the row:
{
label: "Customer",
render: (order) => `${order.firstName} ${order.lastName}`,
}A computed-only column has no adapter field. FlowPanel cannot automatically sort, filter, inline-edit, or include it in field-based export. If those operations matter, expose the value through the data model or a custom adapter/query surface.
Check all consumers
Before shipping a custom column, verify the list, drawer/detail view, export, empty value, long value, and mobile width. hidden affects the table but can leave the field available to another declared surface; expose controls additional values that may cross the generated boundary.
Use a theme slot when the same primitive needs to change everywhere, or a custom page when the entire row experience no longer fits a table.
See Resources reference for generated column types.