#26171·twenty

conditionalAvailabilityExpression in SDK for Widget Visibility (and ideally Tabs too)

Author: admin-laicadevCreated Sep 17, 2026Updated Sep 17, 2026
Labelsprio: medium

How should an app declare record-dependent widget visibility on a page layout?

Context

Self-hosted v2.41.0, twenty-sdk 2.41.0. We extend the standard task object with a discriminator field (taskType) and a group of type-specific fields, and we want the page-layout widget that holds those fields to appear only on records of that type.

Two things exist in the product today:

  • PageLayoutWidgetEntity has both a conditionalDisplay (JsonLogic) column and a conditionalAvailabilityExpression (string) column.
  • The front end evaluates them in evaluateWidgetVisibility.ts: conditionalAvailabilityExpression wins when it is defined, otherwise jsonLogic.apply(conditionalDisplay, context) must be true. buildWidgetVisibilityContext.ts exposes the open record as selectedRecords[0].

The SDK's widget manifest exposes only one of the two:

// [email protected] · dist/define/index.d.ts
type PageLayoutWidgetManifest = SyncableEntityOptions & {
  title: string;
  type: `${WidgetType}`;
  objectUniversalIdentifier?: string;
  conditionalDisplay?: PageLayoutWidgetConditionalDisplay;
  position?: PageLayoutWidgetPosition;
  heightBehavior?: `${PageLayoutWidgetVerticalListHeightBehavior}`;
  configuration: PageLayoutWidgetUniversalConfiguration;
};

conditionalAvailabilityExpression is not on the type, and a value passed anyway is dropped silently by twenty apply — no error, no warning, the widget is created without it. CommandMenuItemManifest does carry the property, which is what made us expect it here too.

We currently hand-write the JsonLogic:

conditionalDisplay: { '==': [{ var: 'selectedRecords.0.taskType' }, 'INFO'] }

That works, but selectedRecords.0.<field> is an undocumented shape we read out of the front-end source, and nothing validates it at apply time — a typo produces a widget that is simply never visible.

Questions

  1. Is conditionalAvailabilityExpression intended to be app-authorable at all, or is conditionalDisplay the supported path for page-layout widgets and the second column internal?
  2. If conditionalDisplay is the supported path, is selectedRecords[0] a stable part of the public contract for record-page widgets?
  3. Is record-dependent visibility meant to reach page-layout tabs as well? PageLayoutTabManifest has no conditional property of any kind, so today the only way to hide a tab is to make every widget in it invisible and rely on getTabsWithVisibleWidgets.ts dropping it.

Desired outcome

An app can declare, in its manifest, that a page-layout widget (and ideally a tab) is visible only for records matching a condition on the record's own fields — using a documented, typed API, where a wrong field reference fails at twenty apply rather than silently producing a widget nobody ever sees.

Reproduction

  1. Add a SELECT field to a standard object from an app.
  2. definePageLayoutWidget({ ..., conditionalAvailabilityExpression: '...' }).
  3. npx twenty apply — succeeds; the widget is created with the column null.