A field with an [up-switch] attribute controls the state of other elements when its value changes.
The switching happens on the client, without a server request.
Common effects are showing, hiding or disabling another element. You can also implement custom switching effects.
Tip
The
[up-switch]attribute handles light, client-side effects. For effects that render on the server, see Reactive server forms.
The controlling form field gets an [up-switch] attribute with a selector for the elements to show or hide:
<select name="level" up-switch=".level-dependent"> <!-- mark: up-switch=".level-dependent" -->
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="expert">Expert</option>
</select>
The target elements use [up-show-for] and [up-hide-for]
attributes to declare for which values they are shown or hidden:
<div class="level-dependent" up-show-for="beginner"> <!-- mark: up-show-for="beginner" -->
shown for beginner level, hidden for other levels
</div>
<div class="level-dependent" up-hide-for="beginner"> <!-- mark: up-hide-for="beginner" -->
hidden for beginner level, shown for other levels
</div>
Elements are switched when the page loads and whenever the field changes.
A hidden element gets a [hidden] attribute.
Fields inside a hidden element are still submitted with the form. To exclude them from the submission, disable them instead.
By default the entire form is searched for matching elements. This can be configured.
You can also toggle an element for multiple values.
The controlling field gets an [up-switch] attribute with a selector
for the fields to disable or enable:
<select name="role" up-switch=".role-dependent">
<option value="trainee">Trainee</option>
<option value="manager">Manager</option>
</select>
The target fields use [up-enable-for] and [up-disable-for]
attributes to declare for which values they are enabled or disabled:
<!-- The department field is only enabled for managers -->
<input class="role-dependent" name="department" up-enable-for="manager"> <!-- mark: up-enable-for="manager" -->
<!-- The mentor field is only enabled for trainees -->
<input class="role-dependent" name="mentor" up-disable-for="manager"> <!-- mark: up-disable-for="manager" -->
When the target is a container, all fields and buttons within are disabled or enabled.
To switch for multiple values, separate them with a space or comma:
<div class="level-dependent" up-show-for="intermediate expert"> <!-- mark: up-show-for="intermediate expert" -->
only shown for intermediate and expert levels
</div>
If your values might contain spaces, you may also serialize them as a relaxed JSON array:
<div class='level-dependent' up-show-for='["John Doe", "Jane Doe"]'> <!-- mark: up-show-for='["John Doe", "Jane Doe"]' -->
You selected John or Jane Doe
</div>
Instead of switching on specific string values, you can use :blank to match an empty input value:
<input type="text" name="user" up-switch=".target">
<div class="target" up-show-for=":blank"> <!-- mark: up-show-for=":blank" -->
please enter a username
</div>
Inversely, :present matches any non-empty input value:
<div class="target" up-hide-for=":present"> <!-- mark: up-hide-for=":present" -->
please enter a username
</div>
For checkboxes you may match against the pseudo-values :checked or :unchecked:
<input type="checkbox" name="flag" up-switch=".flag-dependent">
<div class="flag-dependent" up-show-for=":checked"> <!-- mark: up-show-for=":checked" -->
only shown when checkbox is checked
</div>
<div class="flag-dependent" up-show-for=":unchecked"> <!-- mark: up-show-for=":unchecked" -->
only shown when checkbox is unchecked
</div>
You may also match against the [value] attribute of the checkbox element:
<input type="checkbox" name="flag" value="active" up-switch=".flag-dependent">
<div class="flag-dependent" up-show-for="active"> <!-- mark: up-show-for="active" -->
only shown when checkbox is checked
</div>
When you have multiple checkboxes for a single array field,
set [up-switch] on an element containing all checkboxes:
<div up-switch=".department-dependent"> <!-- mark: up-switch=".department-dependent" -->
<input type="checkbox" name="department[]" value="development"> Development
<input type="checkbox" name="department[]" value="sales"> Sales
<input type="checkbox" name="department[]" value="accounting"> Accounting
</div>
<div class="department-dependent" up-show-for="sales, accounting">
shown when either sales or accounting is checked
</div>
Use [up-switch] on a container for a radio button group:
<div up-switch=".level-dependent"> <!-- mark: <div> -->
<input type="radio" name="level" value="beginner">
<input type="radio" name="level" value="intermediate">
<input type="radio" name="level" value="expert">
</div> <!-- mark: </div> -->
<div class="level-dependent" up-show-for="beginner">
shown for beginner level, hidden for other levels
</div>
<div class="level-dependent" up-hide-for="beginner">
hidden for beginner level, shown for other levels
</div>
By default the entire form is searched for matching elements.
You can narrow or expand the search scope by setting an [up-switch-region]
attribute on the controlling field.
To match all elements within the current layer, set the region to :layer:
<form method="post" action="/order">
<select name="payment" up-switch=".payment-info" up-switch-region=":layer"> <!-- mark: up-switch-region=":layer" -->
<option value="paypal">PayPal</option>
<option value="manual">Manual wire transfer</option>
</select>
</form>
<div class="payment-info" up-show-for="paypal">
You will be redirected to PayPal to complete your order.
</div>
<div class="payment-info" up-show-for="manual">
We will ship your package once we receive your transfer.
</div>
By default switch effects are applied for every input event.
To react to other events like change, see Watch options.
Switching effects are applied immediately and are never debounced, even when you configure a delay for other watchers.
Elements are switched when Unpoly boots, usually on DOMContentLoaded. When your scripts load after the browser has rendered the page,
users may briefly see elements that are hidden a moment later.
To prevent this, you can render the initial [hidden] attributes on the server. If you'd rather not repeat your switching rules in your templates,
hide all switched elements until Unpoly has booted:
html:not(.up-booted) :is([up-show-for], [up-hide-for]) {
display: none;
}
When Unpoly boots, it switches all elements and sets the .up-booted class on the <html> element.
Matching elements are then shown, and the others keep their [hidden] attribute.
Important
When Unpoly cannot boot, e.g. on an unsupported browser or when a script fails to load, these elements stay hidden.
[up-switch] lets you implement your own switching effects.
For example, we want a custom [highlight-for] attribute. It draws a bright
outline around the department field when the manager role is selected:
<select name="role" up-switch=".role-dependent">
<option value="trainee">Trainee</option>
<option value="manager">Manager</option>
</select>
<input class="role-dependent" name="department" highlight-for="manager"> <!-- mark: highlight-for="manager" -->
When the role select changes, an up:form:switch event is emitted on all elements matching .role-dependent.
We can use this event to implement our custom [highlight-for] effect:
up.on('up:form:switch', '[highlight-for]', (event) => {
let highlightedValue = event.target.getAttribute('highlight-for')
let isHighlighted = (event.field.value === highlightedValue)
event.target.style.outline = isHighlighted ? '2px solid orange' : '' // mark: outline
})
The event is also emitted when the page loads, so elements start in the correct state.
See up:form:switch for all event properties and when it is emitted.
Tip Your coding agent can search these docs locally with the Unpoly agent skill