Skip to main content

reference Field Type

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

Use relatedEntity to declare what kind of entity the FK points to, and clickable: true to make the resolved label interactive — firing a reference-click interaction event that the host application can handle.

Core Examples​

Basic Reference Rendering​

Assignee and Project resolve IDs to names via options. relatedEntity declares the target entity type.

Task
Assignee
Project
Task: API redesignMateo SilvaPlatform v2
Task: Onboarding flowPriya NairGrowth
Task: Billing exportAurora ChenPlatform v2
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: 'reference', options: userOptions, relatedEntity: 'user' },
{ key: 'project', label: 'Project', type: 'reference', options: projectOptions, relatedEntity: 'project' },

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 redesignAurora Chen
Task: Onboarding flowMateo Silva
Task: Billing export
{ key: 'reviewer', label: 'Reviewer', type: 'reference', options: userOptions, relatedEntity: 'user' },

Condition​

Use condition to suppress the field entirely for rows where the reference 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 redesignMateo SilvaAurora Chen
Task: Onboarding flowPriya NairMateo Silva
Task: Billing exportAurora Chen
{
key: 'reviewer',
label: 'Reviewer',
type: 'reference',
options: userOptions,
relatedEntity: 'user',
condition: (entity) => entity.reviewer !== null && entity.reviewer !== undefined,
},

Clickable References​

Set clickable: true to render the resolved label as an interactive element. Clicking (or pressing Enter/Space) fires a reference-click interaction event. The host application receives the event and decides what to do — navigate, open a drawer, fetch related data.

Assignee and Project labels are clickable. Click one to see the interaction event in the browser console.

Task
Assignee
Project
Task: API redesignMateo SilvaPlatform v2
Task: Onboarding flowPriya NairGrowth
Task: Billing exportAurora ChenPlatform v2
{
key: 'assignee',
label: 'Assignee',
type: 'reference',
options: userOptions,
relatedEntity: 'user',
clickable: true,
},

The interaction event payload:

{
kind: 'reference-click',
interactionId: 'reference-click:assignee',
interactionLabel: 'Assignee reference clicked',
entity: { /* full row */ },
fieldKey: 'assignee',
fieldValue: 'user-2', // raw FK from entity
fieldLabel: 'Mateo Silva', // resolved display label
relatedEntity: 'user', // entity type declared on the field
zone: 'content',
}

The host application pattern:

interactions: {
onEvent: (ctx) => {
if (ctx.kind === 'reference-click') {
navigate(`/${ctx.relatedEntity}/${ctx.fieldValue}`);
// or: openDrawer(ctx.relatedEntity, ctx.fieldValue);
}
},
},

Field Options Reference​

OptionTypeDefaultEffect
keystringrequiredEntity property holding the foreign key or reference ID.
labelstringunsetColumn header or card label.
type'reference'—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.
relatedEntitystringunsetDeclares the entity type this field points to (e.g. 'user', 'project'). Forwarded in reference-click events and reserved for future resolvers.
clickablebooleanfalseWhen true, renders the label as an interactive element and fires reference-click on interaction.
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 reference rendering. Use link for static hyperlinks built from the entity data.

Why reference vs select?​

Both types use the same options lookup for display. The difference is intent and future capability:

  • select — a field constrained to a fixed domain list (status, priority, category). The options represent every valid value.
  • reference — a FK pointing to a separate entity in another collection. The options are a local snapshot used for display. relatedEntity declares what it points to; clickable enables navigation.

Future Widgemo capabilities would treat these differently: reference fields would support live resolvers, navigation, and entity-aware filtering; select fields would drive local enumeration 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.
  • clickable: true requires a interactions.onEvent handler on the Widgemo config — without it the click fires but nothing happens.
  • formatter output is what gets matched against options.value — not the original raw value.

Performance​

:::tip Stable options reference widgemo-core builds an internal Map from your options array for O(1) lookups. This Map is only rebuilt when the options reference changes. Define your options array outside your render function — or memoize it with useMemo if constructed dynamically — so the Map is reused across renders.

// Good — stable reference, Map built once
const userOptions = users.map(u => ({ value: u.id, label: u.name })); // computed once

// In a React component, memoize if users can change:
const userOptions = useMemo(
() => users.map(u => ({ value: u.id, label: u.name })),
[users]
);

// Avoid — new array every render, Map rebuilt every render
// options={users.map(u => ({ value: u.id, label: u.name }))}

Also pre-filter options to only the FK values present in your dataset — there is no need to pass 10,000 user records when only 47 distinct users appear in the current view. :::

See Also​