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.
Begin with a regular <form> element, with standard [method] and [action] attributes.
To have Unpoly handle the submission, set an [up-submit] attribute:
<form method="post" action="/subscribers" up-submit> <!-- mark: 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 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.
To update an element other than the main fragment, set an [up-target]
attribute with a CSS selector:
<form method="post" action="/comments" up-submit up-target="#comments"> <!-- mark: up-target="#comments" -->
<textarea name="text"></textarea>
<button type="submit">Post comment</button>
</form>
<div id="comments"> <!-- mark: id="comments" -->
<!-- chip: 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], so you don't need to set both.
The same goes for other rendering attributes like [up-layer] or [up-transition].
A target can also address multiple fragments, append to an existing element, or resolve relative to the form. See Targeting fragments for everything a target selector can express.
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 (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 for rendering errors elsewhere, validating single fields while the user
fills in the form, and other ways to detect a failed submission.
To confirm a successful submission or show an error, include an [up-flashes]
element with your message in the response:
<div up-flashes>
<strong>Your comment was posted!</strong> <!-- mark-line -->
</div>
The [up-flashes] element on the page is updated with any response, even when the form targets
a different fragment. See Notification flashes for placing it in your layout and clearing old messages.
Submitting a form is considered navigation 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 for all navigation defaults and how to customize them.
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.
A submit button with [name] and [value] attributes contributes its value
to the form parameters sent to the server:
<form method="post" action="/proposal" up-submit>
<button type="submit" name="decision" value="accept">Accept</button> <!-- chip: Sends { decision: 'accept' } -->
<button type="submit" name="decision" value="reject">Reject</button> <!-- chip: Sends { decision: 'reject' } -->
</form>
To send multiple params, set an [up-params] attribute on the button:
<button type="submit" up-params="{ decision: 'reject', reason: 'Poor quality' }">Reject</button> <!-- mark: up-params="{ decision: 'reject', reason: 'Poor quality' }" -->
A submit button can send the form to a different server endpoint,
by setting standard [formaction] and [formmethod] attributes:
<form method="post" action="/proposal/accept" up-submit> <!-- mark: action="/proposal/accept" -->
<button type="submit">Accept</button>
<button type="submit" formaction="/proposal/reject">Reject</button> <!-- mark: formaction="/proposal/reject" -->
</form>
A submit button can supplement or override most [up-...] attributes from the form:
<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> <!-- mark: up-confirm="Really reject?" -->
</form>
See [up-submit] for a list of overridable attributes.
An individual submit button can opt for a full page load, by setting an [up-submit="false"] attribute:
<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> <!-- mark: up-submit="false" -->
</form>
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] attribute on the form:
<form method="get" action="/projects" up-autosubmit up-target=".projects"> <!-- mark: up-autosubmit -->
<input type="search" name="query">
<select name="status">
<option value="active">Active</option>
<option value="archived">Archived</option>
</select>
</form>
<div class="projects">
<!-- chip: 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] on that field instead of the form.
To wait until the user has stopped typing, or to react to other events, see Watch options.
To run your own JavaScript when a field changes, use [up-watch].
To update parts of the form while the user is still filling it in, without saving anything,
use [up-validate] instead.
While the form is submitting, you can show the user that it is processing: disable its fields and buttons, style it with feedback classes, show a placeholder or run a preview. See Loading state for an overview.
To submit a form element programmatically, pass it to up.submit():
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 to supplement
or override the form's attributes:
up.submit(form, { target: '#elsewhere', transition: 'cross-fade' })
To update fragments without a form element, use up.render().
You can configure Unpoly to handle all forms on a page without requiring an [up-submit] attribute:
up.form.config.submitSelectors.push('form')
Individual forms can opt out by setting an [up-submit="false"] attribute.
See Handling all forms for the exceptions under this setting.
Tip Your coding agent can search these docs locally with the Unpoly agent skill