On this page
The shape of the problem
A form asks "What can we help with?" — and the follow-up questions should differ depending on the answer. A design enquiry needs different fields from a support request.
Showing all of them at once produces a wall of mostly-irrelevant inputs. Conditional logic shows only what applies.
Webflow has no native conditional fields, but the browser has everything needed. The build is straightforward. What isn't obvious is the failure mode in the middle of this article, which breaks forms in a way that's genuinely hard to diagnose.
Markup
Declare the condition on the wrapper, not the input:
<select name="enquiry-type" id="enquiry-type">
<option value="">Please choose…</option>
<option value="design">Design project</option>
<option value="support">Support request</option>
<option value="other">Something else</option>
</select>
<div data-show-when="enquiry-type" data-show-value="design" hidden>
<label for="budget">Approximate budget</label>
<select name="budget" id="budget" data-conditional-required>...</select>
</div>
<div data-show-when="enquiry-type" data-show-value="support" hidden>
<label for="account-email">Account email</label>
<input name="account-email" id="account-email" type="email" data-conditional-required>
</div>
<div data-show-when="enquiry-type" data-show-value="design support" hidden>
<label for="timeline">When do you need this?</label>
<input name="timeline" id="timeline">
</div>
Three conventions doing the work: data-show-when names the controlling field, data-show-value lists the values that reveal this block (space-separated, so one block can serve several answers), and data-conditional-required marks fields required only while visible.
In Webflow, add these as custom attributes on a div block, and set the initial hidden state there too.
The required-field trap
Here's the part that costs people an afternoon.
If a hidden field is marked required, the browser refuses to submit the form. It tries to focus the invalid field to explain why — and it cannot, because the field is hidden. Depending on the browser you get a console warning, or absolutely nothing.
The symptom: clicking submit does nothing at all. No error, no network request, no message. The form is simply inert, and nothing in the Designer looks wrong.
So conditional fields must have required added and removed as they show and hide, never set statically in the markup. That's what data-conditional-required is for — it records intent, while the actual required attribute is applied only while the field is visible.
This is also worth checking on any existing form that "just stopped working": look for a required field hidden at some breakpoint.
The script
(function () {
const form = document.querySelector('form');
if (!form) return;
const blocks = Array.from(form.querySelectorAll('[data-show-when]'));
if (!blocks.length) return;
function sync() {
blocks.forEach(block => {
const controller = form.elements[block.dataset.showWhen];
if (!controller) return;
const wanted = (block.dataset.showValue || '').split(' ').filter(Boolean);
const current = controller.type === 'checkbox'
? (controller.checked ? 'true' : 'false')
: controller.value;
const visible = wanted.includes(current);
block.hidden = !visible;
block.querySelectorAll('[data-conditional-required]').forEach(field => {
if (visible) {
field.setAttribute('required', '');
} else {
field.removeAttribute('required');
field.value = ''; // don't submit answers to questions no longer asked
}
});
});
}
form.addEventListener('change', sync);
form.addEventListener('input', sync);
sync();
})();
Two decisions worth naming.
Clearing hidden values. When a block hides, its fields are emptied. Otherwise someone selects "design", enters a budget, switches to "support", and submits a support request carrying a budget — confusing in the CRM and occasionally a privacy issue. The trade-off: switch back and forth and their answer is gone. For most forms, correctness wins; if your form is long, store the value in a data attribute and restore it instead.
Running sync() on load. Browsers restore form values on back-navigation and refresh. Without an initial call, someone returning to the page sees "design" selected with the design fields hidden.
Cascading conditions
Conditions that depend on conditional fields work with no extra code, because sync() re-evaluates every block on every change. A block whose controller is itself hidden simply evaluates against an empty value and stays hidden.
The thing to watch is a circular dependency — block A controlled by a field inside block B, which is controlled by a field inside A. Nothing crashes; both just stay hidden forever. If a block never appears, check the chain.
Accessibility
Two additions that take a minute and matter.
Announce the change. Revealing fields is silent to screen reader users. A live region tells them something happened:
<p aria-live="polite" class="visually-hidden" id="form-status"></p>
if (visible && block.hidden === false) {
document.getElementById('form-status').textContent =
'Additional questions added below.';
}
Use hidden, not just a CSS class. The hidden attribute removes the element from the accessibility tree. A block hidden only with opacity: 0 or height: 0 remains focusable — keyboard users tab into invisible fields, which is thoroughly disorienting.
Testing before ship
The specific sequence that catches the common bugs:
- Load the form. Confirm conditional blocks are hidden.
- Choose each option in turn. Confirm the right blocks appear, and only those.
- Fill a conditional field, then change the controlling answer. Confirm the value cleared.
- Submit with a conditional block hidden. This is the one that catches the required trap — if nothing happens, you have a hidden required field.
- Tab through with the mouse untouched. Confirm focus never lands in a hidden block.
- Submit and check the received data contains only the fields that were visible.
Step 4 is the whole reason for the data-conditional-required pattern. It's also the test people skip, because it looks like testing that nothing happens.