Developing an Interactive Form with Branching (Conditional Logic) on Your Website
We often encounter this situation: a user starts filling out a form with 30 fields, most of which are irrelevant to them. They are a legal entity, but they see a field for passport data. They abandon the form halfway through. This situation is a common cause of low conversion. We solve this with conditional logic: fields are shown or hidden based on responses. As a result, each user sees only relevant questions, and conversion increases by 20–40%. According to Baymard Institute, long forms reduce conversion by 80% — conditional logic compensates for this effect. We develop interactive forms with branching turnkey in 4–6 days.
How Conditional Logic Works in a Form
The core idea: the form is governed by rules described in a JSON configuration on the server. The logic can be changed without deploying the frontend — just update the configuration file.
Conditional Logic Engine
interface FieldRule {
field: string; // field to which the rule applies
action: 'show' | 'hide' | 'require' | 'set_value';
conditions: Condition[];
logic: 'and' | 'or';
}
interface Condition {
field: string;
operator: 'equals' | 'not_equals' | 'contains' | 'greater_than' | 'is_empty';
value: unknown;
}
function evaluateCondition(condition: Condition, formValues: Record<string, unknown>): boolean {
const fieldValue = formValues[condition.field];
switch (condition.operator) {
case 'equals': return fieldValue === condition.value;
case 'not_equals': return fieldValue !== condition.value;
case 'contains': return String(fieldValue).includes(String(condition.value));
case 'is_empty': return !fieldValue || fieldValue === '';
case 'greater_than': return Number(fieldValue) > Number(condition.value);
default: return false;
}
}
function shouldShowField(rule: FieldRule, formValues: Record<string, unknown>): boolean {
const results = rule.conditions.map(c => evaluateCondition(c, formValues));
return rule.logic === 'and' ? results.every(Boolean) : results.some(Boolean);
}
Form Component with Conditional Logic
function ConditionalForm({ schema, onSubmit }: Props) {
const { watch, register, handleSubmit } = useForm();
const formValues = watch();
const visibleFields = schema.fields.filter(field => {
const rule = schema.rules.find(r => r.field === field.id);
if (!rule) return true; // no rules — always show
return rule.action === 'show'
? shouldShowField(rule, formValues)
: !shouldShowField(rule, formValues);
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<AnimatePresence>
{visibleFields.map(field => (
<motion.div
key={field.id}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
>
<FormField field={field} register={register} />
</motion.div>
))}
</AnimatePresence>
<button type="submit">Submit</button>
</form>
);
}
Why Store Rules on the Server?
Note: when rules are stored on the server, a marketer or manager can change the form logic through an admin panel without developer involvement. This is critical for A/B tests and quick response to user behavior. Server-side configuration ensures consistent form behavior across all devices and synchronization with backend validation.
Example Configuration
{
"fields": [
{ "id": "contact_type", "type": "radio", "options": ["Individual", "Sole Proprietor", "LLC"] },
{ "id": "company_name", "type": "text", "label": "Company Name" },
{ "id": "inn", "type": "text", "label": "TIN" },
{ "id": "passport", "type": "text", "label": "Passport Data" }
],
"rules": [
{
"field": "company_name",
"action": "show",
"logic": "or",
"conditions": [
{ "field": "contact_type", "operator": "equals", "value": "Sole Proprietor" },
{ "field": "contact_type", "operator": "equals", "value": "LLC" }
]
},
{
"field": "passport",
"action": "show",
"logic": "and",
"conditions": [
{ "field": "contact_type", "operator": "equals", "value": "Individual" }
]
}
]
}
The configuration is stored on the server — logic changes without deployment.
Comparison: Form with Conditional Logic vs Static Form
| Characteristic | Static Form | Form with Branching |
|---|---|---|
| Number of fields for the user | Always maximum | Only relevant (reduction by 40–60%) |
| Flexibility of changes | Requires deployment | Changes via JSON configuration |
| Conversion | Baseline | Higher by 20–40% |
| Implementation complexity | Low | Medium (requires engine) |
The branching form outperforms the static one in conversion by 1.5–2 times: according to our data, conversion is 20–40% higher, and the number of fields is reduced by 40–60%.
Additional Table: Condition Operators
| Operator | Description | Example |
|---|---|---|
equals |
Value equality | "contact_type" == "Sole Proprietor" |
not_equals |
Inequality | "status" != "active" |
contains |
Substring inclusion | "email" contains "@" |
greater_than |
Numeric greater than | "age" > 18 |
is_empty |
Field is empty | "phone" is empty |
How to Avoid Performance Issues?
With many fields (50+) and complex rules, condition checks can cause delays. The solution is memoization of computed fields and debounce on watch. In our engine, each shouldShowField call is cached until the dependent field changes. This reduces rendering load to a few milliseconds even on forms with a hundred fields. Additionally, we use a 300ms debounce on watch to avoid unnecessary recalculations.
Typical Implementation Mistakes
The first mistake is not accounting for edge cases. For example, when changing a selection that already hid other fields, those fields' values need to be properly cleared. The second is lack of server-side validation: client-side logic can be bypassed, so all rules are duplicated on the backend. The third is complex dependency chains: if field A depends on B, and B depends on A, a circular dependency arises. This must be detected at configuration load time. The fourth is lack of logging rule firings, which complicates debugging.
Work Process
- Analytics — discuss scenarios, draw a mind map of branching.
- Design — define JSON rule schema, determine field types.
- Implementation — write the engine, components, and configurator.
- Testing — check all condition chains, including edge cases.
- Deployment — deploy to production, grant access to the configurator.
- Support — answer questions and fix bugs for 30 days after delivery.
What's Included in Development
- Design of JSON rule schema and field types
- Development of the conditional logic engine (React components + server parser)
- Integration with your CRM or any API (Bitrix24, AmoCRM, HubSpot)
- Admin panel for managing rules without deployment
- Configuration documentation
- Training managers to use the configurator
- 30-day technical support
Timelines and Cost
A form with a conditional logic engine and JSON configuration is developed in 4–6 business days. The cost is calculated individually, depending on integration complexity and number of scenarios. The timeline may increase with complex integrations or non-standard animations — we evaluate each project separately. Custom React forms with server configuration are our specialty, with over 50 implementations.
Order a form with conditional logic for your project — we will analyze the task within one business day. Get a consultation — we will analyze your task and offer the optimal solution. Our engineers have over 10 years of web development experience and over 50 branching form implementations. We guarantee stable operation and fast support after delivery. Budget savings from using server configuration amount to up to 50% on modifications, and the cost is fixed in the contract.







