How-To Guide

Dynamic Forms That Actually Work

January 2026 · 16 min read

A client fills out your service inquiry form. They select "Commercial" instead of "Residential" and suddenly the form transforms - square footage fields appear, questions about loading docks materialize, the pricing section restructures itself. They didn't refresh the page. They didn't click a button. The form just... knew.

That's dynamic forms working the way they should. But here's the thing - for every form that adapts gracefully, there are dozens that turn into confusing messes. Fields appear out of nowhere. Required markers show up on inputs users can't even see. The submit button stays disabled and nobody knows why.

This guide covers two types of dynamic behavior that, when done right, make forms feel intelligent: conditional logic (showing and hiding based on answers) and repeating sections (letting users add multiple items). Both are powerful. Combined, they're transformative. But both can create disasters if you're not thoughtful about implementation.

Part 1: Conditional Logic

The simplest way to think about conditional logic: the form changes based on what users tell you. Not after they submit - right there, in real-time, as they're filling things out. This isn't just a nice feature. It's the difference between a form that respects people's time and one that wastes it.

Think about a photography booking form. If someone selects "Headshot Session" why would you ask them about wedding venues? If they pick "Real Estate Photography" questions about family group sizes make no sense. Without conditional logic, you either create separate forms for each service (maintenance nightmare) or force everyone through questions that don't apply to them (user experience nightmare).

The Many Faces of Conditional Behavior

Most people think of conditional logic as show/hide - a field appears or disappears based on a previous answer. That's the most common type, but it's not the only one. A dropdown's options might change based on an earlier selection. A field might be required in some situations but optional in others. Labels and helper text might update to match the context. Even entire pages in a multi-step form can be skipped when they're not relevant.

Consider a cleaning service quote form. When someone selects "Residential" you show bedrooms and bathrooms. When they select "Commercial" those fields vanish and you show square footage and number of floors instead. But it goes deeper than that. The "frequency" dropdown might offer different options - residential clients see "Weekly, Bi-weekly, Monthly" while commercial clients see "Daily, 3x Weekly, Weekly." The pricing display might switch from "per visit" to "per month." The contact section might change from asking for a home address to asking for a business name and billing contact.

All of this happens without the user doing anything except answering that first question honestly. The form reshapes itself around their needs.

Learn conditional visibility patterns in FormTs.

Patterns That Users Understand

The key to good conditional logic is predictability. Users should never wonder "why did this field just appear?" The connection between their action and the form's response needs to be obvious.

The classic example is the "Other" option. Every dropdown with an "Other" choice should reveal a text field when selected. Users expect this. They've seen it a thousand times. When they click "Other" and a text box smoothly appears asking them to specify, it feels natural. No explanation needed.

Yes/No questions work the same way. "Do you have dietary restrictions?" is clearly going to lead somewhere if you answer Yes. "Are you booking for a group?" obviously opens up group-related questions. "Need parking?" will ask about vehicles. The pattern is so established that the conditional behavior feels like part of the language of forms.

Service type branching follows this same intuition. When a user selects a specific service from a list, they expect to see questions about that service. A pet grooming form where selecting "Dog" reveals breed and size questions while selecting "Cat" reveals coat length questions makes immediate sense. The user chose the path; the form is just following their lead.

Pro tip

Good conditional logic is invisible. Users shouldn't think "oh, this form has conditional logic." They should just feel like the form is asking the right questions. When someone notices the mechanics, something's probably wrong.

Where Conditional Logic Goes Wrong

The most common disaster is the hidden required field. Somewhere in your conditional logic, you have a field that's required. Somewhere else, you have logic that hides it. Now you have a user staring at a submit button that won't work, with no visible errors, because there's a required field they literally cannot see or fill out.

This usually happens when requirements are added after the form is built. Someone decides the company field should be required for business customers. Someone else had already set up logic to hide the entire business section under certain conditions. Nobody tested the intersection.

Circular dependencies are another killer. Field A is visible when Field B has a certain value. Field B is visible when Field A has a certain value. This seems obviously broken when I describe it, but in complex forms with dozens of conditions, it's easy to accidentally create cycles. The form can end up in impossible states where nothing makes sense.

Then there's the surprise form explosion. A user is filling out what appears to be a simple 5-field form. They answer one question - maybe "Do you need custom requirements?" - and suddenly 15 new fields cascade into view. The simple form just became a complex one, and the user feels ambushed. They started this journey expecting a quick interaction; now they're committed to something much bigger.

If answering a question might dramatically expand the form, users need to know that upfront. Either warn them ("Selecting 'Custom' will require additional details") or better yet, use a multi-step form where new pages are expected rather than surprising.

Testing Conditional Forms

Static forms have one path. Dynamic forms have many. A form with three yes/no conditional triggers has eight possible paths. Add service type selection with four options and you're at thirty-two. This combinatorial explosion is why conditional forms break in production - developers test the happy path and maybe one alternative, missing the weird combination where everything falls apart.

Before building a conditional form, map out every path. Write them down. Service A with add-ons enabled. Service A without add-ons. Service B with the group booking flag. Service B without it. Every combination that's possible needs to be listed and later tested.

Testing isn't just "does the field appear." It's completing the entire form through that path. Submitting it. Checking the data. A field might appear correctly but not actually save its value. Validation might work on one path and silently fail on another. You won't catch these bugs by clicking around - you need to complete full journeys.

Part 2: Repeating Sections

Conditional logic handles "show this or that." Repeating sections handle "show as many as needed." How many guests are attending your event? How many line items in this order? How many vehicles need detailing? The answer varies per user, sometimes dramatically.

Without repeating sections, you're stuck making bad choices. You could ask "how many guests?" and then show a giant text area for names. Good luck parsing that data. You could create fields for Guest 1, Guest 2, through Guest 20, making most users scroll past 18 empty sections. Or you could limit everyone to 3 guests and lose the big bookings.

Repeating sections let users add exactly what they need. Start with one guest section. Click "Add another guest." A second section appears with the same fields. Add as many as needed, up to whatever limit makes sense. Remove the ones you don't need. The form fits the user's situation instead of forcing the user into a predetermined structure.

When Repeating Sections Make Sense

The clearest use case is multiple people. Event registrations need attendee details for each person attending. Household applications need information about each family member. Team bookings need names and roles for everyone involved. Each person is a structured set of fields - name, email, phone, maybe dietary needs or accessibility requirements - repeated for however many people there are.

Order forms are another natural fit. Each line item has a product, quantity, and options. A catering order might have multiple dishes, each with its own quantity and special instructions. A service quote might have multiple services requested, each with its own specifications. The structure is consistent; only the count varies.

Multiple addresses come up more often than you'd expect. Billing versus shipping is obvious, but think about a delivery service that needs multiple drop-off points. Or a moving company that needs multiple pickup locations. These are usually capped at a small number - nobody needs 50 addresses - but the flexibility is essential.

Learn how to build add/remove fields in FormTs.

Getting the UX Right

The biggest mistake with repeating sections is making the add/remove controls hard to find. Users need to immediately understand how to add another instance and how to remove one they don't need. An "Add another guest" button should be obvious - positioned right after the last guest section, clearly labeled, visually distinct. The remove button (usually an X or trash icon) should be on each instance, positioned consistently.

Visual grouping matters enormously. Each instance needs clear boundaries - a card, a bordered section, significant spacing. When you have three guests entered, it should be immediately obvious where Guest 1 ends and Guest 2 begins. Without clear separation, the form becomes a wall of fields where users lose track of what belongs to what.

Numbering helps more than you'd think. "Guest 1 of 3" tells users where they are. It helps when they need to go back and edit something ("I need to fix Guest 2's email"). It helps when validation fails ("There's an error in Vehicle 3"). Without numbering, users are navigating blind.

Empty states deserve attention. What does the form look like before any instances are added? Starting with one instance already visible is usually best - it shows users what to expect and feels more welcoming than a blank area with an "Add your first item" button. But if instances are optional (like additional emergency contacts), starting empty with a clear call to action works too.

Pro tip

Set limits and communicate them. "Add guest (max 20)" is honest and helpful. Users know the boundaries. Unlimited sounds flexible but causes problems - performance issues, forms that scroll forever, confused users who added 50 instances by mistake and can't figure out how to clean up.

Validation Gets Interesting

Each instance in a repeating section validates independently. If email is required in the guest section, it's required for every guest added. This seems obvious until you realize the implications for error messages. "Email is required" isn't helpful when there are five guests - which one? Errors need to reference the specific instance: "Guest 3: Email is required."

Cross-instance validation is trickier. Maybe you don't allow duplicate emails across guests. Maybe time slots can't overlap. Maybe you need at least one item marked as "primary." These validations have to look across all instances, not just within one. The error messages get more complex: "Guest 4 has the same email as Guest 1."

Then there's the question of different requirements for different instances. Maybe the first contact needs full details - name, email, phone, role - but additional contacts only need name and email. Maybe the primary shipping address requires full validation but secondary addresses can be partial. You can handle this with conditional required logic that checks the instance index or a "primary" flag.

Calculations That Span Instances

Repeating sections often feed into totals. An order form sums up line item prices. An event registration calculates cost based on number of attendees. A cleaning quote estimates time based on how many rooms are added.

Running totals that update in real-time are satisfying. User adds a line item, the total updates immediately. They can see the impact of each addition. This builds trust and helps them make decisions - "maybe I don't need that third service after all."

Count-based pricing gets more sophisticated. 1-5 guests at $100 per person. 6-10 at $90. 11+ at $80. As users add guests, they watch the per-person price drop. This encourages larger bookings and feels like a reward for bringing more people. The math happens automatically; users just see the result.

Part 3: Combining Both Powers

Here's where it gets really interesting. Conditional logic inside repeating sections. The number of instances affecting form-wide visibility. Different instances getting different treatment based on their content.

Picture an event registration where each guest can have dietary restrictions. There's a checkbox: "Dietary restrictions?" Check it, and a text field appears - but only for that guest. Guest 1 might have the expanded section while Guests 2 and 3 show just the checkbox. The conditional logic runs independently per instance.

Or a quote builder where each line item can be standard or custom. Standard items show a product dropdown with preset prices. Custom items show a description field and manual price entry. Each line item chooses its own path through the conditional logic, independent of the others.

When Instance Count Triggers Changes

The number of instances can drive form-wide behavior. Zero attendees? Show a message explaining that at least one is required. More than 10? Display a bulk discount banner. More than 50? Hide the self-service booking entirely and show "Contact us for large events" instead.

This creates natural breakpoints in the user experience. Small orders flow through one path. Large orders get handled differently. The transitions can be smooth - the discount banner appears automatically when they hit the threshold, disappears if they remove items and drop below it.

Instance count can also affect validation. Maybe orders under $100 can proceed with basic info, but orders over $100 require phone number verification. Maybe bookings for 1-3 people can be instant, but larger groups need approval and additional fields for group coordinator contact.

First Instance vs. Subsequent Instances

Sometimes the first instance is special. The primary contact needs more information than additional contacts. The main shipping address has stricter validation than alternate addresses. The lead traveler needs full passport details while companions only need names.

You can handle this by checking the instance index (first vs. others) or by having a "primary" toggle that makes one instance the main one. Either way, the form adapts - showing more fields for the primary instance, fewer for the rest.

This keeps the form lean. Additional instances are lightweight, collecting just the essentials. Users don't feel burdened adding a second or third item because those sections are simpler. But the first instance, the important one, gets the thorough treatment it needs.

Pro tip

Test combinations thoroughly. Add 3 instances. Use conditional fields differently in each. Remove the middle one. Add another. Change the first instance's conditional trigger. These edge cases are where bugs hide. Repeating sections plus conditional logic multiplies the test surface significantly.

Building With FormTs

Most form builders treat dynamic behavior as a special feature. You build your static form first, then layer on conditional logic through a separate system - event listeners, state management, special "reactive" wrappers. FormTs takes a different approach: reactivity is the foundation, not an add-on.

Look at almost any property in FormTs configuration - label, isVisible, isRequired, options, min, max, placeholder - and you'll see it accepts either a static value or a function. Want a fixed label? Pass a string. Want the label to change based on another field? Pass a function that returns a string. Same property, same API, no special reactive syntax to learn.

This design means dynamic forms aren't harder to build than static ones. You're not adding complexity when you make something reactive - you're just changing a value to a function. A dropdown with fixed options and a dropdown whose options depend on another field look almost identical in code. The reactivity is invisible until you need it.

Visibility works this way too. isVisible on a field, a row, an entire subform - pass true for always visible, false for always hidden, or a function that checks whatever conditions matter. FormTs tracks the dependencies automatically. When the referenced field changes, the visibility updates. No manual subscriptions, no event wiring.

The same pattern applies to validation. isRequired can be a boolean or a function. A field that's required only when another checkbox is checked? One function. A field that's required only when the order total exceeds a threshold? One function. The validation state stays in sync with the form state without you having to orchestrate it.

For repeating sections, you use the same primitives: addSubform() to create new instances, remove() to delete them, and buttons with click handlers to trigger these operations. A counter variable tracks IDs, an array tracks what exists. It's explicit and flexible - you control exactly how add/remove works. And because every property supports functions, conditional logic inside these dynamic sections works naturally - each instance evaluates its own visibility rules based on its own values.

This is what makes combining conditional logic with repeating sections straightforward instead of painful. You're not fighting two different systems. Everything uses the same reactive foundation. A function that checks instance count to show a bulk discount banner works exactly like a function that checks a checkbox to show additional fields. The patterns compose.

Common Questions

Should hidden fields keep their values when they become visible again?

It depends on context. For temporary hiding (collapsible sections, tab switches), keep values - users expect their input to survive. For conditional branching where the field becomes irrelevant (switching from Business to Personal), clearing makes sense to avoid submitting stale data. FormTs keeps values by default; you can configure clearing behavior per field.

How many repeating instances should I allow?

Set limits based on realistic use cases, not technical capacity. Event attendees: 10-20 usually covers family and friend groups. Order line items: 50-100 handles most real orders. Addresses: 3-5 is plenty for shipping scenarios. Always show the limit ('Add guest - max 20') so users know the boundaries upfront.

How do I handle validation errors in repeating sections?

Always reference the specific instance in error messages. 'Email is required' is useless with 5 guests - say 'Guest 3: Email is required.' For cross-instance validation (like unique emails), explain the conflict: 'Guest 4 has the same email as Guest 1.' Position error messages near the relevant instance, not at the form level.

Can I nest repeating sections inside each other?

Technically possible, but approach with extreme caution. Nested repeating sections (orders containing items containing options) create exponential complexity in both UX and data handling. If you find yourself needing this, consider whether a different data structure or multi-step flow might work better. One level of repetition is manageable; two levels gets confusing fast.

Ready to Build Dynamic Forms?

Start with a template or describe what you need and let AI build it for you.