Skip to main content

FEEL Function Reference

Forma uses FEEL (Friendly Enough Expression Language) for dynamic expressions. This page provides a complete reference of available functions and operators.

Context Variables

In FEEL expressions, you can access form data using these patterns:

PatternDescriptionExample
fieldNameDirect field accessage >= 18
computed.nameComputed field valuecomputed.bmi > 25
ref.pathReference data lookupref.taxRates.CA
valueCurrent field value (in validation)value >= 0
item.fieldNameArray item field (in array context)item.quantity > 0
itemIndexArray item index (0-based)itemIndex = 0

Operators

Arithmetic

OperatorDescriptionExample
+Additionprice + tax
-Subtractiontotal - discount
*Multiplicationquantity * unitPrice
/Divisiontotal / count
**Exponentiationbase ** 2

Comparison

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

Logical

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

String Functions

string length

Returns the length of a string.

{
"rule": "string length(value) >= 3",
"message": "Must be at least 3 characters"
}

substring

Extracts a portion of a string.

substring(string, start)
substring(string, start, length)
{
"expression": "substring(phone, 1, 3)"
}

Note: FEEL uses 1-based indexing for strings.

upper case / lower case

Convert string case.

{
"expression": "upper case(code)",
"expression": "lower case(email)"
}

contains

Check if a string contains a substring.

{
"visibleWhen": "contains(email, \"@company.com\")"
}

starts with / ends with

Check string prefix or suffix.

{
"visibleWhen": "starts with(phone, \"+1\")"
}
{
"visibleWhen": "ends with(email, \".edu\")"
}

matches

Test a string against a regular expression pattern.

{
"rule": "matches(value, \"^[A-Z]{2}[0-9]{4}$\")",
"message": "Must be 2 letters followed by 4 digits (e.g., AB1234)"
}

String Concatenation

Use + to concatenate strings.

{
"expression": "firstName + \" \" + lastName"
}

Numeric Functions

abs

Returns the absolute value.

{
"expression": "abs(difference)"
}

floor / ceiling

Round down or up to nearest integer.

{
"expression": "floor(price)",
"expression": "ceiling(quantity)"
}

round

Round to specified precision.

round(number)
round(number, scale)
{
"expression": "round(total, 2)"
}

min / max

Find minimum or maximum in a list.

{
"expression": "min([price1, price2, price3])",
"expression": "max([score1, score2])"
}

sum

Sum all values in a list.

{
"expression": "sum([item1, item2, item3])"
}

mean

Calculate the average of a list.

{
"expression": "mean([score1, score2, score3])"
}

Date Functions

today

Returns the current date.

{
"rule": "date(value) > today()",
"message": "Date must be in the future"
}

now

Returns the current datetime.

{
"rule": "date and time(value) < now()",
"message": "Must be a past datetime"
}

date

Parses a string into a date.

{
"expression": "date(dateOfBirth)",
"rule": "date(endDate) > date(startDate)"
}

Date Components

Extract parts from a date.

{
"expression": "year(date(dateOfBirth))",
"expression": "month(date(startDate))",
"expression": "day(date(endDate))"
}

Date Arithmetic

Subtract dates to get duration.

{
"expression": "date(endDate) - date(startDate)"
}

List Functions

count

Returns the number of items in a list.

{
"visibleWhen": "count(selectedItems) > 0"
}

all / any

Check if all or any items match a condition.

{
"rule": "all items in items satisfy item.quantity > 0",
"message": "All items must have positive quantity"
}

append

Add an item to a list.

{
"expression": "append(existingList, newItem)"
}

flatten

Flatten nested lists.

{
"expression": "flatten([[1, 2], [3, 4]])"
}

Reference Data Functions

get value

Dynamic lookup in reference data using a field value as the key.

{
"referenceData": {
"taxRates": {
"CA": 0.0725,
"NY": 0.08,
"TX": 0.0625
}
},
"computed": {
"stateTax": {
"expression": "get value(ref.taxRates, state)"
}
}
}

Static Path Access

For fixed paths, use dot notation.

{
"expression": "ref.constants.taxRate"
}

Nested Lookup

Access nested properties from dynamic lookups.

{
"referenceData": {
"products": {
"SKU001": { "price": 9.99, "weight": 0.5 },
"SKU002": { "price": 19.99, "weight": 1.2 }
}
},
"computed": {
"productPrice": {
"expression": "get value(ref.products, selectedSku).price"
}
}
}

Conditional Expressions

if-then-else

Basic conditional.

{
"expression": "if age >= 18 then \"adult\" else \"minor\""
}

Chained conditions

{
"expression": "if score >= 90 then \"A\" else if score >= 80 then \"B\" else if score >= 70 then \"C\" else \"F\""
}

Null Handling

Null Check

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

Safe Operations

Handle null values with conditionals.

{
"expression": "if quantity != null then quantity * price else 0"
}

Null Coalescing

{
"expression": "if discountRate = null then 0 else discountRate"
}

Common Patterns

Age Calculation

{
"expression": "year(today()) - year(date(dateOfBirth))"
}

Percentage Calculation

{
"expression": "round((completed / total) * 100, 1)"
}

Conditional Discount

{
"expression": "if subtotal >= 100 then subtotal * 0.9 else subtotal"
}

Status Based on Value

{
"expression": "if computed.bmi < 18.5 then \"Underweight\" else if computed.bmi < 25 then \"Normal\" else if computed.bmi < 30 then \"Overweight\" else \"Obese\""
}

Expression Debugging

If your expression isn't working as expected:

  1. Check field names - Ensure exact spelling match
  2. Verify data types - Numbers and strings behave differently
  3. Handle nulls - Unset fields return null
  4. Test incrementally - Start simple and add complexity

Common Errors

ErrorCauseSolution
Expression returns nullField value is nullAdd null check
Type mismatchComparing number to stringUse consistent types
Field not foundTypo in field nameCheck field exists in schema
Syntax errorInvalid FEEL syntaxCheck quotes and operators