Conditional Form Fields — Dynamic Form HTML CSS JS
Conditional Form Fields · Forms · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Conditional Form Fields — Show/Hide Inputs by Choice with Scoped Validation

Long forms that ask everyone every question are a leading cause of abandonment. Conditional fields fix that by revealing only the inputs relevant to the path a user chose — a sales enquiry asks for company size, a support request asks for an order ID, and neither shows the other's fields. This snippet builds that dynamic-form pattern in plain HTML, CSS, and vanilla JavaScript, with the one detail most implementations get wrong: validating only the fields that are actually visible.
Declarative conditions in the markup
Each conditional group carries a data-when attribute naming the dropdown value that reveals it (data-when="sales", data-when="support"). When the topic changes, applyConditions() simply toggles a .show class on each group based on whether its data-when matches the current value — there's no per-group if statement to maintain. Adding a new branch is a markup change (a new group with the right data-when), not a JavaScript change, which keeps the logic flat no matter how many paths the form grows.
Validation that follows visibility
The critical correctness detail: a required field that's hidden must not block submission. visibleRequiredFields() collects [data-req] inputs *only* from groups currently showing (plus the always-on message box), so the submit button enables when the relevant fields are filled — and a hidden support field never traps a sales enquiry. Tying required-ness to visibility is the difference between a form that submits and one that's mysteriously stuck because an off-screen field is empty. The submit button stays disabled until every *visible* required field has a value, giving constant, honest readiness feedback.
A smooth reveal without measuring height
Animating a collapsible section open has historically meant measuring its content height in JavaScript. This snippet uses the modern CSS grid trick instead: the group is a grid with grid-template-rows: 0fr collapsed and 1fr expanded, with the inner content set to overflow: hidden; min-height: 0. Transitioning between 0fr and 1fr animates the section to exactly its natural height with no JavaScript measurement at all — clean, content-agnostic, and smooth. Opacity fades alongside it for polish.
Reset behaviour that prevents stale data
Switching topics hides the previous branch's fields. Those inputs keep their values in the DOM (so switching back restores them), but because validation only counts visible fields, a half-filled hidden branch never affects the current submission. On submit, you collect only the fields relevant to the chosen topic — the FAQs cover scoping the payload so you don't send a support form's empty company-name field to your sales endpoint.
Accessible and progressive
The conditional groups are real form elements that exist in the DOM whether shown or not, so the markup stays simple and the form degrades gracefully. For full accessibility you'd also toggle aria-hidden and the disabled attribute on hidden inputs (so they leave the tab order entirely) — the FAQs explain how to layer that on. The whole form is driven by two listeners (change on the selector, input for live validation), keeping the behaviour easy to follow and port.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You don't have to trace the visibility-to-validation link by hand to trust it's correct. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why visibleRequiredFields() only collects data-req inputs from groups carrying the show class, and why that specific detail is what prevents a hidden branch from ever blocking submission. The same assistant can help optimize it — for instance asking whether the grid-template-rows 0fr-to-1fr collapse trick has any edge cases with dynamically-inserted content, or whether hidden fields should also get aria-hidden and disabled for full accessibility rather than just being visually collapsed. It is also useful for extending the form: ask it to support nested conditions (a field that only appears when a previous conditional field has a specific value), add a second dropdown that branches further, or wire the submit handler to POST only the fields relevant to the chosen path. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a form with conditional, dropdown-driven field groups in plain HTML, CSS, and JavaScript — no form library, no framework.
Requirements:
- A topic dropdown, plus several field groups each marked with a data attribute naming the exact dropdown value that should reveal it (e.g. data-when="sales"), so adding a new conditional branch requires only a new markup group and a new option, never a new line of JavaScript branching logic.
- On every change of the dropdown, toggle a "show" class on each group based purely on whether its data-when value matches the current selection — no group-specific if/else chain.
- Animate each group's reveal and collapse using CSS grid-template-rows transitioning between 0fr and 1fr (with the inner content wrapped in its own element using overflow hidden), so the section expands and collapses to its exact natural content height with zero JavaScript height measurement and no fixed max-height guess.
- Mark specific inputs inside each conditional group as required via a data attribute, and implement validation so that only required fields inside groups that are CURRENTLY visible can block form submission — a required field belonging to a hidden, unselected branch must never prevent the submit button from enabling.
- The submit button must be disabled by default and re-evaluate its disabled state live on every input event, enabling exactly when the topic is chosen and every currently-visible required field has a non-empty value.
- Include at least one field that is shared/always-visible regardless of which topic is chosen, to demonstrate mixing always-on fields with conditional ones.
- On submit, prevent the default action, disable all form controls, and show a confirmation message in place of the form.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA contact form renders with a topic dropdown and no extra fields until you choose a topic.
- 2Pick a topicChoose "Sales enquiry" — company and team-size fields glide open; choose "Technical support" and order-ID/urgent fields appear instead.
- 3Watch scoped validationThe Send button stays disabled until every visible required field is filled — hidden branches never block it.
- 4Switch topicsChange the dropdown — the previous branch collapses and the new one reveals; the button re-evaluates against the now-visible fields.
- 5SubmitWith all visible required fields filled, Send enables; submitting shows a confirmation and locks the form.
- 6Add your own branchesAdd a new <div class="cff-cond" data-when="yourvalue"> group and a matching <option> — no JavaScript changes needed.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Tie required-ness to visibility: only validate [data-req] fields inside groups that currently have the .show class (plus any always-on fields), as visibleRequiredFields() does here. A hidden field's value is irrelevant to the current path, so it must be excluded from the readiness check — this is the single most common bug in conditional forms.
Beyond hiding them visually, set the disabled attribute and aria-hidden="true" on inputs inside collapsed groups (and remove those when shown). Disabled fields skip the tab order and aren't submitted, and aria-hidden keeps screen readers from announcing off-screen inputs. Toggle both alongside the .show class in applyConditions().
On submit, read the selected topic and collect values only from the visible groups (and shared fields), building a payload scoped to that path — don't send a support form's empty company field to your sales endpoint. Iterate the .show groups' inputs, or maintain a per-topic field map.
Animating max-height requires guessing a value larger than any content (which makes the timing feel off and clips tall content), while measuring exact height needs JavaScript. The grid 0fr→1fr transition animates to the content's true natural height automatically, with no measurement and no magic numbers — it adapts to any content length.
In React, hold the selected value in useState and conditionally render each group with {value === 'sales' && <SalesFields/>}, computing form validity from the currently-rendered required fields; in Vue, use v-if with a computed valid flag; in Angular, use *ngIf and reactive-forms validators toggled per branch. The framework's conditional rendering replaces the .show class, and validity follows what's rendered.