Unpoly adds CSS classes to links and forms that are loading content, and to the fragments they are targeting. The classes have no effect on their own. They exist so you can style them, to instantly signal that the app is working.
We have a link targeting a fragment #target:
<a href="/path" up-target="#target">Link</a>
<div id="target">
Initial content
</div>
When the user clicks the link, the link is instantly assigned the .up-active class.
The targeted fragment (the #target element) gets the .up-loading class:
<a href="/path" up-target="#target" class="up-active">Link</a> <!-- mark: class="up-active" -->
<div id="target" class="up-loading"> <!-- mark: class="up-loading" -->
Initial content
</div>
Both classes are removed when the request ends.
We have a form targeting a fragment #target:
<form action="/action" up-target="#target">
<input type="text" name="email">
<button type="submit">Submit</button>
</form>
<div id="target">
Initial content
</div>
When the user clicks the submit button, both the button and the form are marked as .up-active
while the form is submitting. The targeted fragment is marked as .up-loading:
<form action="/action" up-target="#target" class="up-active"> <!-- mark: class="up-active" -->
<input type="text" name="email">
<button type="submit" class="up-active">Submit</button> <!-- mark: class="up-active" -->
</form>
<div id="target" class="up-loading"> <!-- mark: class="up-loading" -->
Initial content
</div>
Tip
To also prevent the user from interacting with the form while it submits, see Disabling forms while working.
Sometimes a form is not submitted by pressing a submit button, but by interacting with an input field:
Return inside a focused field.[up-autosubmit] is changed.In these cases, that field is considered the origin element that
caused the submission. It is also marked as .up-active, in addition to the form and its default submit button:
<form action="/action" up-submit class="up-active"> <!-- mark: up-active -->
<input type="text" name="email" class="up-active"> <!-- mark: up-active -->
<button type="submit" class="up-active">Submit</button> <!-- mark: up-active -->
</form>
To instantly signal that a user interaction has been registered, consider highlighting active links and submit buttons in your CSS:
.up-active:is(a, [up-follow], input[type=submit], button[type=submit], button:not([type])) {
outline: 2px solid blue;
}
To convey which parts of the page have a pending update, consider styling targeted fragments:
.up-loading {
opacity: 0.6;
}
You can show a hidden loading message while a form is submitting:
.spinner {
display: none;
form.up-active & {
display: block;
}
}
If CSS alone is not enough, previews let you make arbitrary changes to the page while a request is loading. See Loading state for all strategies.
Feedback classes remain set while the request is loading.
Once the response is received, the .up-active and .up-loading classes are removed:
<a href="/path" up-target="#target">Link</a>
<div id="target">
New content from server
</div>
Feedback classes are also removed if the request ends for any other reason, such as the server rendering an error code or the request being aborted. See How previews end for all reasons that end a request.
When content is rendered from the cache, no .up-active or .up-loading classes are set, since there is nothing to wait for.
When the cache entry is stale, Unpoly automatically reloads the fragment.
During this cache revalidation Unpoly marks the targeted fragment as .up-revalidating instead of .up-loading:
<div id="target" class="up-revalidating"> <!-- mark: class="up-revalidating" -->
Possibly stale content
</div>
When rendering from JavaScript, Unpoly sets .up-active on the origin element
that caused the interaction. For example, when following a link with up.follow(), that link
is .up-active while loading:
up.follow(link)
In situations where Unpoly cannot guess the origin, you can pass it as an { origin } option.
For example, when a button click causes a render pass, that button should be the origin:
up.on('click', '.my-button', function(event) {
up.render({ url: '/path', origin: event.target }) // mark: origin
})
To set additional classes on activated origins, push a class name to up.status.config.activeClasses:
up.status.config.activeClasses.push('activated')
To set additional classes on targeted fragments that are loading new content, push
a class name to up.status.config.loadingClasses:
up.status.config.loadingClasses.push('fetching')
To set additional classes during cache revalidation, push a class name to up.status.config.revalidatingClasses:
up.status.config.revalidatingClasses.push('checking')
To disable feedback classes, set an
[up-feedback=false] attribute:
<a href="/path" up-follow up-feedback="false">Link</a> <!-- mark: up-feedback="false" -->
From JavaScript you may pass a { feedback: false } option:
up.follow(link, { feedback: false }) // mark: { feedback: false }
Tip Your coding agent can search these docs locally with the Unpoly agent skill