Skip to main content

relation Field Type

type: 'relation' displays a reference to another entity by resolving its ID against an options array. The raw value (typically a foreign key or ID string) is matched with strict equality (===) against each option's value property, and the corresponding label is shown. If no match is found, the raw value is displayed as a plain string.

The rendering is identical to type: 'select'. The distinction is semantic: relation signals a cross-entity reference (a foreign key, an owner ID, a linked record), whereas select signals a constrained value from a fixed domain list.

Core Examples​

Basic Relation Rendering​

Assignee and Project columns resolve IDs to human-readable names via options arrays.

Task
Assignee
Project
Task: API redesignuser-2proj-1
Task: Onboarding flowuser-3proj-2
Task: Billing exportuser-1proj-1
const userOptions = [
{ value: 'user-1', label: 'Aurora Chen' },
{ value: 'user-2', label: 'Mateo Silva' },
{ value: 'user-3', label: 'Priya Nair' },
];

{ key: 'assignee', label: 'Assignee', type: 'relation', options: userOptions },
{ key: 'project', label: 'Project', type: 'relation', options: projectOptions },

Fallback for Unresolved References​

When the raw value is null, undefined, or an ID not present in options, it renders as a plain string. A null reviewer renders as the empty string ''.

Billing export has no reviewer — the null value resolves to an empty string.

Task
Reviewer
Task: API redesignuser-1
Task: Onboarding flowuser-2
Task: Billing export
{ key: 'reviewer', label: 'Reviewer', type: 'relation', options: userOptions },

Condition​

Use condition to suppress the field entirely for rows where the relation is empty, rather than showing a blank cell.

Reviewer column only renders for rows where a reviewer is assigned. Billing export gets no Reviewer cell.

Task
Assignee
Reviewer
Task: API redesignuser-2user-1
Task: Onboarding flowuser-3user-2
Task: Billing exportuser-1
{
key: 'reviewer',
label: 'Reviewer',
type: 'relation',
options: userOptions,
condition: (entity) => entity.reviewer !== null && entity.reviewer !== undefined,
},

Field Options Reference​

OptionTypeDefaultEffect
keystringrequiredEntity property holding the foreign key or reference ID.
labelstringunsetColumn header or card label.
type'relation'—Resolves the raw value against options. Falls back to String(value) on no match.
options{ value, label }[]unsetThe resolution table. value is matched with === against the raw field value.
formatter(value, entity) => unknownunsetRuns before the options lookup — use to normalise IDs before matching.
condition(entity) => booleanunsetHides the field for rows where the function returns false. Useful to suppress empty references.
align'left', 'center', 'right''left'Text alignment in table mode.
widthCSS length or numberunsetColumn width in table mode.
showLabeltrue, falsetrue when label is setShows or hides the label in card/grid layouts.
renderAsstringunsetReplace relation rendering. Use link to make the resolved label a clickable hyperlink.

Why relation vs select?​

Both types use identical rendering logic today. The difference is intent:

  • select — a field constrained to a fixed domain list (status, priority, category). The options represent every valid value for that field.
  • relation — a reference to an external entity, identified by a key (user ID, project ID, record slug). The options are a local copy of a remote list, used only for display.

Future Widgemo capabilities would treat these differently: relation fields would logically connect to external data sources for live lookups or navigation, while select fields would drive local filter menus.

Caveats​

  • The match is strict (===). A string ID 'user-1' will not match a numeric option { value: 1 }.
  • null and undefined values render as the empty string '' — use condition to hide those rows instead.
  • formatter output is what gets matched against options.value — not the original raw value.

See Also​