Skip to main content

Conditional Logic

Forma uses FEEL expressions to control dynamic field behavior. Three properties control when fields are visible, required, or editable.

Properties

PropertyDescriptionDefault
visibleWhenShow field when expression is trueAlways visible
requiredWhenRequire field when expression is trueUses schema required
enabledWhenAllow editing when expression is trueAlways enabled
readonlyWhenMake field read-only when expression is trueAlways editable

visibleWhen

Control when a field is displayed:

{
"dependentField": {
"label": "Dependent Field",
"visibleWhen": "triggerField = true"
}
}

Examples

Show based on checkbox:

{
"visibleWhen": "hasInsurance = true"
}

Show based on selection:

{
"visibleWhen": "country = \"US\""
}

Show based on number:

{
"visibleWhen": "age >= 18"
}

Show based on multiple conditions:

{
"visibleWhen": "hasInsurance = true and insuranceType = \"health\""
}

Hidden Field Behavior

  • Hidden fields are not validated
  • Hidden fields retain their values (not cleared)
  • Hidden fields are excluded from submission if not visible

requiredWhen

Make a field conditionally required:

{
"insuranceNumber": {
"label": "Insurance Number",
"visibleWhen": "hasInsurance = true",
"requiredWhen": "hasInsurance = true"
}
}

Priority

  • requiredWhen overrides the schema's required array
  • If both schema required and requiredWhen are set, requiredWhen takes precedence

Examples

Required based on selection:

{
"requiredWhen": "paymentMethod = \"credit_card\""
}

Required based on checkbox:

{
"requiredWhen": "needsShipping = true"
}

enabledWhen

Control when a field can be edited:

{
"approvedBy": {
"label": "Approved By",
"enabledWhen": "status = \"pending\""
}
}

Disabled fields:

  • Are visible but grayed out
  • Cannot be edited by the user
  • Retain their existing value

Examples

Disable after submission:

{
"enabledWhen": "submissionStatus != \"submitted\""
}

Enable only for specific users:

{
"enabledWhen": "userRole = \"admin\""
}

readonlyWhen

Make a field conditionally read-only:

{
"invoiceNumber": {
"label": "Invoice Number",
"readonlyWhen": "status = \"approved\""
}
}

Readonly fields differ from disabled fields (enabledWhen: false):

  • Readonly: Visible and focusable, values are submitted, but the field cannot be edited
  • Disabled: Field appears greyed out and cannot be interacted with

Examples

Read-only after approval:

{
"readonlyWhen": "approvalStatus = \"approved\""
}

Read-only for non-admins:

{
"readonlyWhen": "userRole != \"admin\""
}

FEEL Expression Syntax

Comparison Operators

OperatorDescriptionExample
=Equalstatus = "active"
!=Not equalstatus != "archived"
>Greater thanage > 18
>=Greater or equalage >= 21
<Less thanprice < 100
<=Less or equalprice <= 50

Logical Operators

OperatorDescriptionExample
andBoth conditions trueage >= 18 and hasLicense = true
orEither condition truestatus = "active" or status = "pending"
notNegationnot(isBlocked)

String Values

Strings must be quoted with escaped quotes:

{
"visibleWhen": "country = \"US\""
}

Null Checks

{
"visibleWhen": "email != null"
}

Referencing Other Fields

Reference fields directly by name:

{
"visibleWhen": "firstName != null and lastName != null"
}

Referencing Computed Values

Use the computed. prefix:

{
"visibleWhen": "computed.total > 1000"
}

Common Patterns

Show Additional Fields

{
"otherReason": {
"label": "Please specify",
"visibleWhen": "reason = \"other\""
}
}

Conditional Sections

Group related fields with the same condition:

{
"companyName": {
"label": "Company Name",
"visibleWhen": "customerType = \"business\""
},
"taxId": {
"label": "Tax ID",
"visibleWhen": "customerType = \"business\""
}
}

Progressive Disclosure

Reveal fields as users complete previous ones:

{
"step2Field": {
"visibleWhen": "step1Field != null"
},
"step3Field": {
"visibleWhen": "step2Field != null"
}
}

Mutual Exclusion

Show one field OR another:

{
"phoneNumber": {
"visibleWhen": "contactMethod = \"phone\""
},
"emailAddress": {
"visibleWhen": "contactMethod = \"email\""
}
}