A Web Component for Conditionally Displaying Fields

Building on my recent work in the form utility space, I’ve created a new web component that allows you to conditionally display form fields based on the values of other fields: form-show-if.

This component tackles a common UX pattern that HTML doesn’t natively support. You know the scenario—you have a form where certain fields should only appear when specific conditions are met. Maybe you want to show shipping address fields only when someone checks “Ship to different address,” or display a text input for “Other” when someone selects that option from a dropdown. This web component makes that setup effortless — and declarative.

You set up form-show-if like this:

<form-show-if conditions="contact_method=phone">
  <label for="phone">Phone Number
    <input type="tel" id="phone" name="phone">
  </label>
</form-show-if>

You wrap any field and its label in the component and then declare the conditions under which it should be displayed in the conditions attribute.

Defining the display conditions

Each condition is a key/value pair where the key aligns to the name of the field you need to observe and the value is the value that triggers the display. If any value should trigger the display, use an asterisk (*) as the value. In the example above, the field will become visible only if — in a theoretical contact method choice — a user chooses “phone” as the method they want used.

The conditions attribute can be populated with as many dependencies as you need. Multiple conditions are separated by double vertical pipes (||), as in this example:

<form-show-if conditions="contact_method=phone||contact_method=text_message">
  <label for="phone-number">Phone Number
    <input type="tel" id="phone" name="phone">
  </label>
</form-show-if>

Here the field depends on one of the following conditions being true:

  1. the field matching [name="contact_method"] has a value of “phone” or
  2. the field matching [name="contact_method"] has a value of “text_message”

If the field you reference doesn’t exist, no errors will be thrown—it will just quietly exit.

Customizing the show/hide behavior

By default, the component uses the hidden attribute to hide the wrapped content when it’s not needed. But you can customize this behavior using CSS classes instead:

<form-show-if 
  conditions="shipping-method=express"
  disabled-class="fade-out"
  enabled-class="fade-in">
  <label for="delivery-date">Express Delivery Date
    <input type="date" id="delivery-date" name="delivery-date">
  </label>
</form-show-if>

When using custom classes:

  • disabled-class is applied when the condition is not met (field should be hidden)
  • enabled-class is applied when the condition is met (field should be shown)

Both are optional. Just remember that if you define a disabled-class, the hidden attribute will not be used — you will need to accessibly hide the content yourself.

This gives you complete control over the visual presentation. You could use CSS transitions for smooth animations, apply different styling states, or integrate with your existing design system’s utility classes.

Handling form state properly

The component doesn’t just toggle visibility—it also manages the form state correctly. When fields are hidden, they’re automatically disabled using the disabled attribute. If there are any sibling fields in the component, they will be disabled as well. This prevents these fields from being submitted with the form and ensures they don’t interfere with form validation.

When conditions are met and fields become visible, they’re re-enabled automatically. This behavior works seamlessly with both native form validation and custom validation scripts.

Real-world examples

Here are some practical use cases where this component shines:

“Other” option handling:

<fieldset>
  <legend>How did you hear about us?</legend>

  <label><input type="radio" name="source" value="google"> Google</label>
  <label><input type="radio" name="source" value="friend"> Friend</label>
  <label><input type="radio" name="source" value="other"> Other</label>
  
  <form-show-if conditions="source=other">
    <label for="source-other">Please specify
      <input type="text" id="source-other" name="source-other">
    </label>
  </form-show-if>
</fieldset>

Specific value matching:

<form-show-if conditions="email=test@example.com">
  <label for="debug-info">Debug Information
    <textarea id="debug-info" name="debug-info"></textarea>
  </label>
  <small>This field only shows for test accounts</small>
</form-show-if>

Progressive enhancement in action

Like all good web components, form-show-if follows progressive enhancement principles. If JavaScript fails to load or the browser doesn’t support custom elements, your form still works—users just see all the fields all the time. Not ideal for the user experience, but nothing breaks either.

The component is lightweight, has no dependencies, and works in all modern browsers.

Demo

I’ve put together a comprehensive demo showing various use cases and configurations over on GitHub.

The demo includes examples of:

  • Basic show/hide functionality
  • Multiple condition logic
  • Custom CSS class integration
  • Complex form scenarios with radio buttons and checkboxes
  • Different field grouping approaches

Grab it

You can view the entire project (and suggest enhancements) over on the form-show-if component’s GitHub repo. The component is available as both a standard script and an ES module, so you can integrate it however works best for your project.

Installation is straightforward—just include the script in your page and start using the form-show-if element. No build step required, no framework dependencies, just clean, standards-based progressive enhancement.


Webmentions

  1. This element should be in everyone’s toolkit – I’ve built a few of these in the past. FWIW, my approach was for the HTML attribute to accept a JSON object then to serialize the form to JSON using FormData() and see if the two intersect.
  2. I do like this because of the fact it takes an expression as a parameter, worries me that eventually you'll end up writing an expression interpreter to handle brackets, && etc.
  3. Instead of relying on libraries of 3rd party components, shouldn’t we be encouraging directly making use of Web Component API for progressive enhancement?
  4. Like it would be better to wrap dynamic parts of a form in specific custom WC. Egimplements any logic inside, showing/hiding child elements, postcode-lookup/select-all-checkbox-options type scenarios.
  5. That’s the great thing about web components: you can build whatever you need. If this is useful for you, great! If it becomes a sub-component of a larger component that’s for for your purpose, wonderful! If it’s a springboard to building something else, fantastic!
  6. True, I think more should be made of that. I am worried that 3rd party Web comp libs and design systems (and WC Frameworks like Lit) will be the next jQuery/react/npm maintenance headache in a few years
  7. There are a lot of cases where a good expression language is really useful. I wish HTML had one baked in. We'll definitely need one if we ever get declarative custom elements. Meanwhile, I do have a battle-tested one here, evolved from the Polymer/Dart days: www.npmjs.com/package/@hex...
  8. One issue, your code will only work when the Web Component is defined AFTER all DOM was parsed, because the connectedCallback fires on the OPENING tag. Thus when you define your Web Component EARLY (to prevent FOUCs) there is no innerHTML JSFiddle and link to blogpost: jsfiddle.net/WebComponent...
  9. @Aaron I was literally about to write the same thing for a form I’m building.???? Saved me some time!

  10. A very clever web component that’ll make your conditional form fields a breeze to put together. www.aaron-gustafson.com/notebook/a-w...

Shares

  1. David's Alter (Algo) Ego
  2. zeldman
  3. Zach Leatherman
  4. Andy Bell
  5. jrlarsen
  6. Dan Wellman
  7. answer ✨
  8. Zach Leatherman :11ty:
  9. Francis Rubio
  10. Tyler Sticka
  11. Patrick Grey
  12. David O'Brien
  13. Axel Rauschmayer
  14. Wes
  15. Simon Praetorius
  16. adamghill
  17. Kashyap