Conditions System
Drive field visibility, requirement, and step navigation from live form data.
Conditions control field visibility, requirement, disabled/readonly state, and workflow step navigation. They re-evaluate live as values change.
The when() Function
when(fieldPath) returns a ConditionBuilder:
import { when } from 'rilaykit';
const condition = when('age').greaterThan(18);
condition.evaluate({ age: 25 }); // true
condition.build(); // serializable ConditionConfig, useful for loggingOperators
| Operator | True when |
|---|---|
equals(value) / notEquals(value) | Strict (in)equality |
greaterThan(n) / lessThan(n) | Numeric comparison |
greaterThanOrEqual(n) / lessThanOrEqual(n) | Numeric comparison, inclusive |
contains(value) / notContains(value) | String includes substring, or array includes value |
in(values) / notIn(values) | Field value is (not) in the given list |
matches(pattern) | String matches a RegExp or pattern string |
exists() / notExists() | Value is (not) null/undefined |
Combine with .and() / .or() — nesting groups sub-conditions:
when('userType').equals('premium')
.and(
when('subscription.plan').equals('pro')
.or(when('subscription.legacy').equals(true))
);Field Path Resolution
Paths use dot notation for nested data: when('user.profile.age').greaterThan(18) reads data.user.profile.age. A key that exists verbatim (e.g. a repeatable composite id like items[k0].type) is matched directly before dot-path traversal.
Field-Level Conditions
Fields accept four condition keys: visible, required, disabled, readonly.
form.create(rilay, 'checkout')
.add({
id: 'creditCardNumber',
type: 'text',
props: { label: 'Credit Card Number' },
conditions: {
visible: when('paymentMethod').equals('credit_card'),
required: when('paymentMethod').equals('credit_card'),
},
})
.add({
id: 'email',
type: 'email',
props: { label: 'Email Address' },
conditions: {
disabled: when('emailVerified').equals(true),
readonly: when('accountLocked').equals(true),
},
});conditions.required is enforced on its own — no validation block needed. When the condition is true and the field is empty, submit is blocked.
Step-Level Conditions (Workflows)
Steps accept visible and skippable. Reference previous steps' data with "stepId.fieldId":
const workflow = flow.create(rilay, 'user-onboarding')
.step({
id: 'personal-info',
title: 'Personal Information',
formConfig: personalInfoForm,
})
.step({
id: 'payment-info',
title: 'Payment Information',
conditions: {
visible: when('personal-info.planType').equals('premium')
.or(when('company-info.employees').greaterThan(50)),
skippable: when('personal-info.planType').notEquals('premium'),
},
formConfig: paymentForm,
});A hidden step is treated as nonexistent: navigation jumps over it and it is absent from the onComplete(data, meta) payload (meta.skippedSteps / meta.passedSteps record why).
Conditions and Validation
An invisible field is treated as nonexistent: its validation is skipped, its errors are cleared, and it never blocks submit — even if it becomes invisible mid-async-validation.
Best Practices
- Prefer several simple conditions over one deeply nested tree.
- Test conditions against empty,
null, andundefinedvalues — numeric operators returnfalsefor non-numbers. - Avoid circular dependencies between fields' conditions.