Expression Syntax
Expressions are strings used by properties such as visibleIf, enableIf, requiredIf, defaultValueExpression, setValueExpression, choicesVisibleIf, validators, and calculated values.
SurveyJS evaluates expressions while a respondent fills out the form, so conditions and calculated values update as referenced answers change.
Where Expressions Are Used
Section titled “Where Expressions Are Used”Common expression-backed properties include:
- Question, panel, and page logic:
visibleIf,enableIf,requiredIf - Value automation:
defaultValueExpression,setValueIf,setValueExpression,resetValueIf - Choice filtering:
choicesVisibleIf,choicesEnableIf - Matrix and dynamic panel filtering:
rowsVisibleIf,columnsVisibleIf,templateVisibleIf - Validation:
expressionvalidator.expression - Calculations: root
calculatedValues[].expressionandexpressionquestion values - Root actions:
triggers[].expression,triggers[].setToName,triggers[].setValue - Dynamic ranges:
minValueExpression,maxValueExpression
Values and Literals
Section titled “Values and Literals”Reference answers and calculated values with curly braces:
{ "visibleIf": "{hasPets} = true"}SurveyJS uses { and } as variable delimiters by default. Runtime code can customize this globally with settings.expressionVariableDelimiters, but Formspace-authored schema examples should continue to use the default brace syntax unless the host application has explicitly changed the setting.
Use quoted strings, numbers, booleans, and arrays directly in the expression:
{ "visibleIf": "{country} = 'AU' and {age} >= 18", "requiredIf": "{contactMethods} anyof ['email', 'phone']"}Question names that contain hyphens or spaces still go inside braces:
{ "visibleIf": "{nps-score} >= 9"}Nested Value Access
Section titled “Nested Value Access”Use dot and bracket notation when an expression needs values inside compound questions.
Multi-Select and Multiple Text
Section titled “Multi-Select and Multiple Text”{choices[0]}references the first selected value in a checkbox, image picker, ranking, or similar multi-value question.{contact.email}references an item namedemailinside amultipletextquestion namedcontact.
{ "visibleIf": "{selectedProducts[0]} notempty", "requiredIf": "{contact.email} empty"}Matrix Rows and Columns
Section titled “Matrix Rows and Columns”Inside matrix row expressions, use row-scoped placeholders:
{row.columnName}references a cell in the current row.{prevRow.columnName}references the previous row.{nextRow.columnName}references the next row.{rowIndex}is the 1-based row position across all rows.{visibleRowIndex}is the 1-based position across visible rows.{rowName}references the row value.{rowTitle}references the row display text.
From outside a matrix, reference rows and cells by matrix name:
{matrixName.rowName}references a row value in a single-select matrix.{matrixName.rowName.columnName}references a cell in a multi-select matrix.{matrixName[0].columnName}references a dynamic matrix row by zero-based index.{matrixName[-1].columnName}references the last dynamic matrix row.{matrixName-total.columnName}references a total row cell.
{ "rowsVisibleIf": "{row.quantity} > 0", "visibleIf": "{lineItems[-1].amount} > 100"}Dynamic Panels
Section titled “Dynamic Panels”Inside a dynamic panel template, use panel-scoped placeholders:
{panel.questionName}references a question in the current panel.{prevPanel.questionName}references the previous panel.{nextPanel.questionName}references the next panel.{parentPanel.questionName}references a parent dynamic panel when panels are nested.
From outside a dynamic panel, reference a panel by index:
{dependents[0].name}references the first panel.{dependents[-1].name}references the last panel.
{ "type": "paneldynamic", "name": "jobs", "templateElements": [ { "type": "text", "name": "startDate", "inputType": "date" }, { "type": "text", "name": "endDate", "inputType": "date", "validators": [ { "type": "expression", "text": "End date must be after start date.", "expression": "{panel.startDate} < {panel.endDate}" } ] } ]}Element Properties
Section titled “Element Properties”SurveyJS can read element property values in expressions and dynamic text by prefixing the element reference with $, for example {$pageName.title} or {$panelName.visible}. Runtime code can customize this prefix with settings.expressionElementPropertyPrefix or disable the feature by setting it to an empty string.
Operators
Section titled “Operators”Comparison operators are case-sensitive.
| Purpose | Operators | Example |
|---|---|---|
| Empty | empty, notempty |
{email} notempty |
| Equality | =, ==, equal, !=, <>, notequal |
{country} = 'AU' |
| Comparison | <, <=, >, >=, less, lessorequal, greater, greaterorequal |
{age} >= 18 |
| Contains | *=, contains, contain, notcontains, notcontain |
{email} contains '@' |
| Array match | anyof, noneof, allof |
{roles} anyof ['admin', 'owner'] |
| Logical | !, negate, and, &&, or, ` |
|
| Arithmetic | +, -, *, /, %, ^, power |
{quantity} * {unitPrice} |
Use parentheses when mixing logical groups:
{ "visibleIf": "({country} = 'AU' or {country} = 'NZ') and {age} >= 18"}Functions
Section titled “Functions”Call functions directly in the expression. Do not wrap function names in braces.
Logical
Section titled “Logical”iif(condition, valueIfTrue, valueIfFalse)returns one of two values.
{ "expression": "iif({score} >= 80, 'Pass', 'Review')"}currentDate()returns the current date and time.currentYear()returns the current four-digit year.today()returns the current date.today(days)returns the current date shifted by a number of days.getDate(value)converts a supported value to a date.getYear(date)returns the four-digit year from a date.age(date)returns age for a birthdate.year(date),month(date),day(date), andweekday(date)extract date parts.diffDays(start, end)returns the absolute number of calendar days between two dates.dateDiff(start, end, unit)returns a difference indays,hours,minutes,seconds,months, oryears.dateAdd(date, amount, unit)adds or subtractsdays,hours,minutes,seconds,months, oryears.
{ "defaultValueExpression": "today(7)", "validators": [ { "type": "expression", "text": "Trip cannot be longer than 14 days.", "expression": "dateDiff({startDate}, {endDate}, 'days') <= 14" } ]}Aggregation
Section titled “Aggregation”Use basic aggregation for numeric values:
sum(value1, value2, ...)min(value1, value2, ...)max(value1, value2, ...)avg(value1, value2, ...)count(value1, value2, ...)round(number, optionalPrecision)trunc(number, optionalPrecision)
Use ...InArray functions for dynamic matrices and dynamic panels:
sumInArray(arrayQuestion, fieldName, optionalFilter)minInArray(arrayQuestion, fieldName)minInArray(arrayQuestion, fieldName, returnField)minInArray(arrayQuestion, fieldName, optionalFilter)minInArray(arrayQuestion, fieldName, returnField, optionalFilter)maxInArray(arrayQuestion, fieldName)maxInArray(arrayQuestion, fieldName, returnField)maxInArray(arrayQuestion, fieldName, optionalFilter)maxInArray(arrayQuestion, fieldName, returnField, optionalFilter)avgInArray(arrayQuestion, fieldName, optionalFilter)countInArray(arrayQuestion, fieldName, optionalFilter)
For minInArray and maxInArray, the optional third argument can now be either a return field or a filter condition. If you provide a return field and a filter, pass the filter as the fourth argument.
{ "calculatedValues": [ { "name": "orderTotal", "expression": "sumInArray({lineItems}, 'amount', {amount} > 0)" } ]}Runtime performance note: expression questions recalculate on any survey value change by default. Application code can enable settings.expressionQuestionTrackDependencies = true to recalculate expression questions only when their dependencies change.
Text, choices, and element state
Section titled “Text, choices, and element state”substring(text, start, optionalEnd)returns part of a string using zero-based indexes.displayValue(questionName, optionalValue)returns a question’s display text, including choice labels instead of stored values.getComment(questionName, optionalChoiceValue)returns an Other/comment value for a question or choice.propertyValue(questionName, propertyName)returns a runtime property from a question.isContainerReady(containerName, optionalPanelIndex)checks whether a page, panel, or dynamic panel instance passes validation.isDisplayMode()returns true when the form is read-only or displaying a completed response.
Common Patterns
Section titled “Common Patterns”Conditional question
Section titled “Conditional question”{ "type": "comment", "name": "details", "title": "Tell us more", "visibleIf": "{needsFollowUp} = true"}Required only when visible
Section titled “Required only when visible”{ "type": "text", "name": "email", "title": "Email", "inputType": "email", "requiredIf": "{contactMethod} = 'email'"}Calculated value
Section titled “Calculated value”{ "name": "total", "expression": "{quantity} * {unitPrice}"}Dynamic default
Section titled “Dynamic default”{ "type": "text", "name": "dueDate", "inputType": "date", "defaultValueExpression": "today(7)"}Choice visibility
Section titled “Choice visibility”{ "type": "radiogroup", "name": "supportPlan", "choices": [ "starter", "team", { "value": "enterprise", "text": "Enterprise", "visibleIf": "{companySize} >= 100" } ]}Calculated display
Section titled “Calculated display”{ "type": "expression", "name": "totalDisplay", "title": "Total", "expression": "sumInArray({lineItems}, 'amount')"}Generation Notes
Section titled “Generation Notes”- Prefer stable, simple question names because expressions reference
namevalues directly. - Quote string literals, but do not quote numbers or booleans.
- Use
notemptybefore comparing optional values when blank input should not pass the rule. - Use
anyof,noneof, andalloffor checkbox and tagbox answers because those values are arrays. - Use
panelandrowscoped placeholders only in properties that evaluate inside that context. - Prefer a calculated value when several expressions reuse the same derived result.