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

# Forms

Unpoly submits forms and updates a fragment of the current page with the response. Your server keeps rendering plain HTML, and one convention on your backend also makes validation errors work. Fields can also re-render parts of the form while the user is still filling it in.

<nav aria-label="Contents">

#### Contents

- [Submitting in place](#submitting-in-place)
- [Showing validation errors](#showing-validation-errors)
- [Forms that react on the server](#forms-that-react-on-the-server)
- [Switching state on the client](#switching-state-on-the-client)
- [See also](#see-also)

</nav>

## Submitting in place {#submitting-in-place}

Take a regular form, with standard `[method]` and `[action]` attributes, and add an [`[up-submit]`](https://unpoly.com/api/up-submit.md) attribute:

```html
<form method="post" action="/subscribers" up-submit>
  <input type="email" name="email">
  <button type="submit">Subscribe</button>
</form>
```

When the user submits, Unpoly sends the form data to the server. Your server processes it like any form submission, usually responding with a redirect to the next screen. Unpoly extracts the response's [main fragment](https://unpoly.com/api/main.md) and swaps it into the current page. Elements outside the updated fragment keep their state, like unsaved input in other forms.

To update a different fragment, add an [`[up-target]`](https://unpoly.com/api/up-submit.md#up-target) attribute with a CSS selector:

```html
<form method="post" action="/comments" up-submit up-target="#comments">
  ...
</form>

<div id="comments">
  ...
</div>
```

The form stays a standard form. When JavaScript is unavailable, the browser submits it with a full page load.

<nav aria-label="Read more">

- [More: Submitting forms](https://unpoly.com/learn/submitting-forms.md)

</nav>

## Showing validation errors {#showing-validation-errors}

When the user submitted invalid data, re-render the form with error messages and respond with an error code like [HTTP 422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422). Unpoly then ignores the `[up-target]` and updates the `<form>` element itself, so the user sees your messages next to their input.

To validate a field _before_ the form is submitted, set an [`[up-validate]`](https://unpoly.com/api/up-validate.md) attribute:

```html
<form method="post" action="/users" up-submit>
  <fieldset>
    <label for="email">E-mail</label>
    <input type="email" id="email" name="email" up-validate>
  </fieldset>
  ...
</form>
```

When the user leaves the field, Unpoly submits the form with an [`X-Up-Validate`](https://unpoly.com/api/X-Up-Validate.md) header. Your server validates the input without saving it and renders the form again. Only the `<fieldset>` around the field is updated, now with any error message.

<nav aria-label="Read more">

- [More: Validating forms](https://unpoly.com/learn/validation.md)

</nav>

## Forms that react on the server {#forms-that-react-on-the-server}

[`[up-validate]`](https://unpoly.com/api/up-validate.md) has a second use besides showing errors. Since it can target any fragment, a field can re-render other parts of the form when it changes. Pick a continent, and the country select is filled with the matching countries:

```html
<select name="continent" up-validate="#country">
  ...
</select>

<select name="country" id="country">
  ...
</select>
```

The server renders the new form state from the submitted values, using the same HTML templates as always. Unpoly swaps in only the `#country` element. When the user changes fields faster than the network responds, the form still ends up in a consistent state.

<nav aria-label="Read more">

- [More: Reactive server forms](https://unpoly.com/learn/reactive-server-forms.md)

</nav>

## Switching state on the client {#switching-state-on-the-client}

Light effects like showing, hiding or disabling another element need no server request. A field with an [`[up-switch]`](https://unpoly.com/api/up-switch.md) attribute controls elements that declare for which values they are shown:

```html
<select name="payment" up-switch=".payment-dependent">
  <option value="card">Credit card</option>
  <option value="invoice">Invoice</option>
</select>

<input name="card_number" class="payment-dependent" up-show-for="card">
```

The card number field is shown while _Credit card_ is selected and hidden otherwise.

<nav aria-label="Read more">

- [More: Switching form state](https://unpoly.com/learn/switching-form-state.md)

</nav>

## See also {#see-also}

To disable fields and buttons while a form is submitting, see [Disabling forms while working](https://unpoly.com/learn/disabling-forms.md).

To ask before the user discards a form with unsaved changes, see [Preventing loss of unsaved changes](https://unpoly.com/learn/unsaved-changes.md).

To show confirmations or errors after a submission, see [Notification flashes](https://unpoly.com/learn/flashes.md).

To submit all forms through Unpoly without annotating each one, see [Handling all forms](https://unpoly.com/learn/handling-all-forms.md).

To build a date picker, tag editor or other control that works with these features, see [Custom form fields](https://unpoly.com/learn/custom-form-fields.md).

To submit a form whenever a field changes, like in a search or filter bar, see [Submitting when a field changes](https://unpoly.com/learn/submitting-forms.md#autosubmit).

To fine-tune which events trigger a validation and how changes are debounced, see [Watch options](https://unpoly.com/learn/watch-options.md).

<nav aria-label="In this chapter">

## In this chapter {#children-index-title}

- [Submitting forms](https://unpoly.com/learn/submitting-forms.md)
- [Validating forms](https://unpoly.com/learn/validation.md)
- [Reactive server forms](https://unpoly.com/learn/reactive-server-forms.md)
- [Switching form state](https://unpoly.com/learn/switching-form-state.md)
- [Disabling forms while working](https://unpoly.com/learn/disabling-forms.md)
- [Confirmation dialogs](https://unpoly.com/learn/confirmation-dialogs.md)
- [Unsaved changes](https://unpoly.com/learn/unsaved-changes.md)
- [Handling all forms](https://unpoly.com/learn/handling-all-forms.md)
- [Custom fields](https://unpoly.com/learn/custom-form-fields.md)
- [Watch options](https://unpoly.com/learn/watch-options.md)

</nav>

<nav aria-label="Reading order">

- [Previous: Elements that act like links or buttons](https://unpoly.com/learn/faux-interactive-elements.md)
- [Next: Submitting forms](https://unpoly.com/learn/submitting-forms.md)

</nav>

