---
area: "Learn"
parent: "Forms chapter"
url: "https://unpoly.com/learn/switching-form-state"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Forms chapter](https://unpoly.com/learn/forms.md)</nav>

# Switching form state

A field with an [`[up-switch]`](https://unpoly.com/api/up-switch.md) 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](#toggle) or [disabling](#disable) another element. You can also implement [custom switching effects](#custom-effects).

> #### Tip
>
> The [`[up-switch]`](https://unpoly.com/api/up-switch.md) attribute handles light, client-side effects. For effects that render on the server, see [Reactive server forms](https://unpoly.com/learn/reactive-server-forms.md).

<nav aria-label="Contents">

#### Contents

- [Showing or hiding other elements](#toggle)
- [Disabling or enabling fields](#disable)
- [Switching for multiple values](#multiple-values)
- [Switching on blank or present values](#presence)
- [Usage with checkboxes](#checkboxes)
- [Usage with radio buttons](#radio-buttons)
- [Changing the switched region](#region)
- [Reacting to different events](#reacting-to-different-events)
- [Hiding switched elements before booting](#before-booting)
- [Custom switching effects](#custom-effects)

</nav>

## Showing or hiding other elements {#toggle}

The controlling form field gets an [`[up-switch]`](https://unpoly.com/api/up-switch.md) attribute with a selector for the elements to show or hide:

```html
<select name="level" 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]`](https://unpoly.com/api/up-show-for.md) and [`[up-hide-for]`](https://unpoly.com/api/up-hide-for.md) attributes to declare for which values they are shown or hidden:

```html
<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>
```

Elements are switched when the page loads and whenever the field changes. A hidden element gets a [`[hidden]`](https://unpoly.com/api/hidden.md) attribute. Fields inside a hidden element are still submitted with the form. To exclude them from the submission, [disable](#disable) them instead.

By default the entire form is searched for matching elements. This can be [configured](#region).

You can also toggle an element [for multiple values](#multiple-values).

## Disabling or enabling fields {#disable}

The controlling field gets an [`[up-switch]`](https://unpoly.com/api/up-switch.md) attribute with a selector for the fields to disable or enable:

```html
<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]`](https://unpoly.com/api/up-enable-for.md) and [`[up-disable-for]`](https://unpoly.com/api/up-disable-for.md) attributes to declare for which values they are enabled or disabled:

```html
<!-- The department field is only enabled for managers -->
<input class="role-dependent" name="department" up-enable-for="manager">

<!-- The mentor field is only enabled for trainees -->
<input class="role-dependent" name="mentor" up-disable-for="manager">
```

When the target is a container, all fields and buttons within are disabled or enabled.

## Switching for multiple values {#multiple-values}

To switch for multiple values, separate them with a space or comma:

```html
<div class="level-dependent" 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](https://unpoly.com/api/relaxed-json.md) array:

```html
<div class='level-dependent' up-show-for='["John Doe", "Jane Doe"]'>
  You selected John or Jane Doe
</div>
```

## Switching on blank or present values {#presence}

Instead of switching on specific string values, you can use `:blank` to match an empty input value:

```html
<input type="text" name="user" up-switch=".target">

<div class="target" up-show-for=":blank">
  please enter a username
</div>
```

Inversely, `:present` matches any non-empty input value:

```html
<div class="target" up-hide-for=":present">
  please enter a username
</div>
```

## Usage with checkboxes {#checkboxes}

For checkboxes you may match against the pseudo-values `:checked` or `:unchecked`:

```html
<input type="checkbox" name="flag" up-switch=".flag-dependent">

<div class="flag-dependent" up-show-for=":checked">
  only shown when checkbox is checked
</div>

<div class="flag-dependent" up-show-for=":unchecked">
  only shown when checkbox is unchecked
</div>
```

You may also match against the `[value]` attribute of the checkbox element:

```html
<input type="checkbox" name="flag" value="active" up-switch=".flag-dependent">

<div class="flag-dependent" up-show-for="active">
  only shown when checkbox is checked
</div>
```

### Array fields {#array-fields}

When you have multiple checkboxes for a single [array field](https://unpoly.com/api/up.form.config.md#config.arrayParam), set [`[up-switch]`](https://unpoly.com/api/up-switch.md) on an element containing all checkboxes:

```html
<div 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>
```

## Usage with radio buttons {#radio-buttons}

Use [`[up-switch]`](https://unpoly.com/api/up-switch.md) on a container for a radio button group:

```html
<div up-switch=".level-dependent">
  <input type="radio" name="level" value="beginner">
  <input type="radio" name="level" value="intermediate">
  <input type="radio" name="level" value="expert">
</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>
```

## Changing the switched region {#region}

By default the entire form is searched for matching elements. You can narrow or expand the search scope by setting an [`[up-switch-region]`](https://unpoly.com/api/up-switch.md#up-switch-region) attribute on the controlling field.

To match all elements within the current [layer](https://unpoly.com/api/up.layer.md), set the region to [`:layer`](https://unpoly.com/api/layer.md):

```html
<form method="post" action="/order">
  <select name="payment" up-switch=".payment-info" 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>
```

## Reacting to different events {#reacting-to-different-events}

By default switch effects are applied for every [`input`](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) event. To react to other events like `change`, see [Watch options](https://unpoly.com/learn/watch-options.md). Switching effects are applied immediately and are never debounced, even when you configure a delay for other watchers.

## Hiding switched elements before booting {#before-booting}

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]`](https://unpoly.com/api/hidden.md) attributes on the server. If you'd rather not repeat your switching rules in your templates, hide all switched elements until Unpoly has [booted](https://unpoly.com/api/up-booted.md):

```css
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`](https://unpoly.com/api/up-booted.md) class on the `<html>` element. Matching elements are then shown, and the others keep their [`[hidden]`](https://unpoly.com/api/hidden.md) attribute.

> #### Important
>
> When Unpoly cannot boot, e.g. on an [unsupported browser](https://unpoly.com/api/up.framework.isSupported.md) or when a script fails to load, these elements stay hidden.

## Custom switching effects {#custom-effects}

[`[up-switch]`](https://unpoly.com/api/up-switch.md) 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:

```html
<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">
```

When the role select changes, an [`up:form:switch`](https://unpoly.com/api/up:form:switch.md) event is emitted on all elements matching `.role-dependent`. We can use this event to implement our custom `[highlight-for]` effect:

```js
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' : ''
})
```

The event is also emitted when the page loads, so elements start in the correct state. See [`up:form:switch`](https://unpoly.com/api/up:form:switch.md) for all event properties and when it is emitted.

<nav aria-label="Reading order">

- [Previous: Reactive server forms](https://unpoly.com/learn/reactive-server-forms.md)
- [Next: Disabling forms while working](https://unpoly.com/learn/disabling-forms.md)

</nav>

