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

# Submitting forms

Any form can update a fragment of the current page, instead of loading a full new document. Your server handles the submission like any other, and Unpoly swaps the response into the page.

<nav aria-label="Contents">

#### Contents

- [Submitting a form](#submit)
- [Updating a specific fragment](#target)
- [Handling validation errors](#validation)
- [Showing confirmation or error flashes](#flashes)
- [Navigation defaults](#navigation-defaults)
- [Multiple submit buttons](#submit-buttons)
- [Submitting when a field changes](#autosubmit)
- [Showing that the form is processing](#loading)
- [Submitting forms from JavaScript](#script)
- [Handling all forms automatically](#unobtrusive)

</nav>

## Submitting a form {#submit}

Begin with a regular `<form>` element, with standard `[method]` and `[action]` attributes. To have Unpoly handle the submission, set 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. The server responds with a full HTML page, the same way it would for any browser request. Unpoly extracts the response's [main fragment](https://unpoly.com/api/main.md) and replaces the main fragment on the current page. The rest of the page stays untouched.

The response may contain other HTML, or even the entire application layout. Only the main fragment is extracted. Other elements are discarded from the response, and their counterparts on the page are kept unchanged.

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

> #### Tip
>
> Instead of annotating individual forms, you can also configure Unpoly to [handle all forms on the page](https://unpoly.com/learn/handling-all-forms.md).

## Updating a specific fragment {#target}

To update an element other than the main fragment, set 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">
  <textarea name="text"></textarea>
  <button type="submit">Post comment</button>
</form>

<div id="comments">
  <!-- Content will appear here -->
</div>
```

Unpoly finds the `#comments` element in the server response and swaps it into the current page.

An `[up-target]` attribute already implies [`[up-submit]`](https://unpoly.com/api/up-submit.md), so you don't need to set both. The same goes for other rendering attributes like `[up-layer]` or [`[up-transition]`](https://unpoly.com/api/up-transition.md).

A target can also address multiple fragments, append to an existing element, or resolve relative to the form. See [Targeting fragments](https://unpoly.com/learn/targeting-fragments.md) for everything a target selector can express.

## Handling validation errors {#validation}

When the user submitted invalid data, your server should re-render the form with error messages and respond with a status code outside the 2xx range, like [HTTP 422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) (Unprocessable Content). Unpoly then ignores the `[up-target]` attribute and updates the `<form>` element instead, so the user sees your error messages next to their input. See [Validating forms](https://unpoly.com/learn/validation.md) for rendering errors elsewhere, validating single fields while the user fills in the form, and other ways to detect a failed submission.

## Showing confirmation or error flashes {#flashes}

To confirm a successful submission or show an error, include an [`[up-flashes]`](https://unpoly.com/api/up-flashes.md) element with your message in the response:

```html
<div up-flashes>
  <strong>Your comment was posted!</strong>
</div>
```

The [`[up-flashes]`](https://unpoly.com/api/up-flashes.md) element on the page is updated with any response, even when the form targets a different fragment. See [Notification flashes](https://unpoly.com/learn/flashes.md) for placing it in your layout and clearing old messages.

## Navigation defaults {#navigation-defaults}

Submitting a form is considered [navigation](https://unpoly.com/learn/navigation-defaults.md) by default. When a form updates the layer's main fragment, Unpoly behaves like a full page load would: the browser URL and history are updated, and the page scrolls to the top. Focus moves to the new fragment.

See [Navigation defaults](https://unpoly.com/learn/navigation-defaults.md) for all navigation defaults and how to customize them.

## Multiple submit buttons {#submit-buttons}

Just like in regular HTML, a form can have multiple submit buttons. Unpoly submits the form when any of the buttons is clicked. Pressing `Enter` in a text field submits using the first submit button.

### Per-button parameters {#per-button-params}

A submit button with `[name]` and `[value]` attributes contributes its value to the form parameters sent to the server:

```html
<form method="post" action="/proposal" up-submit>
  <button type="submit" name="decision" value="accept">Accept</button> <!-- Sends { decision: 'accept' } -->
  <button type="submit" name="decision" value="reject">Reject</button> <!-- Sends { decision: 'reject' } -->
</form>
```

To send multiple params, set an [`[up-params]`](https://unpoly.com/api/up-submit.md#up-params) attribute on the button:

```html
<button type="submit" up-params="{ decision: 'reject', reason: 'Poor quality' }">Reject</button>
```

### Per-button actions {#per-button-action}

A submit button can send the form to a different server endpoint, by setting standard `[formaction]` and `[formmethod]` attributes:

```html
<form method="post" action="/proposal/accept" up-submit>
  <button type="submit">Accept</button>
  <button type="submit" formaction="/proposal/reject">Reject</button>
</form>
```

### Overriding render options {#per-button-options}

A submit button can supplement or override most `[up-...]` attributes from the form:

```html
<form method="post" action="/proposal" up-submit up-target="#status">
  <button type="submit" name="decision" value="accept">Accept</button>
  <button type="submit" name="decision" value="reject" up-confirm="Really reject?">Reject</button>
</form>
```

See [`[up-submit]`](https://unpoly.com/api/up-submit.md#attributes) for a list of overridable attributes.

### Opting into a full page load {#per-button-opt-out}

An individual submit button can opt for a full page load, by setting an `[up-submit="false"]` attribute:

```html
<form method="post" action="/report/update" up-submit>
  <button type="submit" name="command" value="save">Save report</button>
  <button type="submit" name="command" value="download" up-submit="false">Download PDF</button>
</form>
```

## Submitting when a field changes {#autosubmit}

A search or filter form often has no submit button. It updates its results as soon as the user changes a field. For this, set an [`[up-autosubmit]`](https://unpoly.com/api/up-autosubmit.md) attribute on the form:

```html
<form method="get" action="/projects" up-autosubmit up-target=".projects">
  <input type="search" name="query">
  <select name="status">
    <option value="active">Active</option>
    <option value="archived">Archived</option>
  </select>
</form>

<div class="projects">
  <!-- Filtered projects will appear here -->
</div>
```

Every change to a field submits the form, even each keystroke in the search field. Your server renders the filtered list, and Unpoly swaps the `.projects` element into the page. To submit only when a particular field changes, set [`[up-autosubmit]`](https://unpoly.com/api/up-autosubmit.md) on that field instead of the form.

To wait until the user has stopped typing, or to react to other events, see [Watch options](https://unpoly.com/learn/watch-options.md). To run your own JavaScript when a field changes, use [`[up-watch]`](https://unpoly.com/api/up-watch.md).

To update parts of the form while the user is still filling it in, without saving anything, use [`[up-validate]`](https://unpoly.com/learn/validation.md) instead.

## Showing that the form is processing {#loading}

While the form is submitting, you can show the user that it is processing: [disable](https://unpoly.com/learn/disabling-forms.md) its fields and buttons, style it with [feedback classes](https://unpoly.com/learn/feedback-classes.md), show a [placeholder](https://unpoly.com/learn/placeholders.md) or run a [preview](https://unpoly.com/learn/previews.md). See [Loading state](https://unpoly.com/learn/loading-state.md) for an overview.

## Submitting forms from JavaScript {#script}

To submit a form element programmatically, pass it to [`up.submit()`](https://unpoly.com/api/up.submit.md):

```js
let form = document.querySelector('form.subscribe')
up.submit(form)
```

The form's `[up-...]` attributes are honored, as if the user had submitted the form. You can pass additional [render options](https://unpoly.com/api/up.render.md#parameters) to supplement or override the form's attributes:

```js
up.submit(form, { target: '#elsewhere', transition: 'cross-fade' })
```

To update fragments without a form element, use [`up.render()`](https://unpoly.com/api/up.render.md).

## Handling all forms automatically {#unobtrusive}

You can configure Unpoly to handle _all_ forms on a page without requiring an [`[up-submit]`](https://unpoly.com/api/up-submit.md) attribute:

```js
up.form.config.submitSelectors.push('form')
```

Individual forms can opt out by setting an `[up-submit="false"]` attribute.

See [Handling all forms](https://unpoly.com/learn/handling-all-forms.md) for the exceptions under this setting.

<nav aria-label="Reading order">

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

</nav>

