Conditional logic in React forms
Showing a field only when an earlier answer warrants it is the point where most React forms start to rot. Done as component state it spreads across the file; done as data it stays one property on the field it belongs to.
The problem with doing it in components
Four conditional fields in a form built the usual way. Nothing here is wrong, and it is already hard to read.
function ApplicationForm() {
const [employment, setEmployment] = useState('')
const [hasKids, setHasKids] = useState(false)
const [income, setIncome] = useState(0)
const showEmployer = employment === 'employed' || employment === 'part-time'
const showBusiness = employment === 'self-employed'
const showDependants = hasKids
const showSupport = hasKids && income < 40000
return (
<form>
{/* ...and every one of these has to stay in sync by hand */}
{showEmployer && <EmployerFields />}
{showBusiness && <BusinessFields />}
{showDependants && <DependantFields />}
{showSupport && <SupportFields />}
</form>
)
}The rules live in one place, the fields in another, and the state in a third. Adding a fifth condition means touching all three. Nothing tells you that showSupport depends on two answers, and nothing stops the next person adding a rule that contradicts an existing one.
The same rule as data
The condition becomes a property of the field it governs. There is no state to keep in sync, because the rule is evaluated from the answers the engine already holds.
{
"id": "employer",
"type": "short_text",
"label": "Employer name",
"showIf": {
"field": "employment",
"operator": "in",
"value": ["employed", "part-time"]
}
}That is the whole change. The field knows when it applies, so reading the field tells you the rule — and deleting the field deletes the rule with it.
Combining conditions
Conditions nest with AND and OR, to any depth. This is the showSupport rule from the first example, which took two pieces of derived state.
{
"id": "support",
"type": "single_select",
"label": "Do you receive support payments?",
"showIf": {
"combine": "AND",
"conditions": [
{ "field": "hasKids", "operator": "eq", "value": true },
{ "field": "income", "operator": "lt", "value": 40000 }
]
}
}Branching whole sections
Conditions are not only for fields. A section can decide where the form goes next, which is how a questionnaire skips a branch that does not apply rather than showing empty steps.
{
"id": "plan",
"title": "Choose a plan",
"exitAction": {
"rules": [
{
"condition": { "field": "type", "operator": "eq", "value": "enterprise" },
"jumpTo": "enterprise-details"
}
],
"default": "payment"
}
}Because the engine owns navigation, progress and step counts follow the branch automatically — the thing that is most often wrong in hand-built multi-step forms.
What you can compare
25 operators, covering equality, ordering, set membership, presence, substring and pattern matching, dates, array contents and length.
Equality & order
eq neq gt gte lt lte betweenSets & presence
in notIn exists notExists isEmpty isNotEmptyText
contains notContains startsWith endsWith matches matchesRegexDates & arrays
dateAfter dateBefore arrayContains arrayNotContains lengthGreaterThan lengthLessThanThe same expression drives showIf, disabled, conditional requiredand section jump rules — one syntax to learn, not four.
Why the order matters
A field’s visibility can depend on an answer that is itself computed from other answers. That makes the rules a dependency graph, not a list, and evaluating them in the order they happen to be written gives stale results on the first pass.
An engine resolves the graph instead. It is the part that is tedious to get right by hand and invisible when it works — which is why hand-built conditional forms tend to be correct until the day someone adds a rule in the wrong place.
Try it
The conditional demo on the homepage runs the real renderer against a real schema — tick the box and watch the field appear.
Related
- What is a form engine?The category this belongs to.
- Building a multi-step surveyBranching and navigation end to end.
- Why schema-driven formsThe wider argument for forms as data.