A target is a CSS selector that names the fragment to update. Unpoly matches it in both the current page and the server response, then swaps the old element for the new one. A target can also address several fragments, append to a list, or pick the match closest to the link the user clicked.
Set an [up-target] attribute with a CSS selector on a link or form:
<a href="/posts/5" up-target=".content">Read post</a> <!-- mark: up-target=".content" -->
<div class="content">
Post will appear here
</div>
<div class="other">
This fragment will not change.
</div>
The server response is expected to include an element matching .content.
The response may include other HTML, even an entire HTML document, but only the
element matching .content is updated on the page. Other elements from the response are discarded, except hungry elements.
Many JavaScript functions take a { target } option to indicate what fragment should be updated:
up.render({ target: '.content', url: '/posts/5' })
Most links and forms update the site's primary content area, called the main fragment. Omit the target to update it:
<a href="/posts/5" up-follow>Read post</a>
<main>
Post will appear here
</main>
The :main selector names the main fragment explicitly, which is useful when it is one of multiple targets.
Likewise, a JavaScript function that omits its { target } option updates the main fragment:
up.render({ url: '/posts/5' })
The main fragment is often, but not always, the HTML <main> element.
Any element with an [up-main] attribute can be the main fragment, and so can the layer's topmost swappable element.
The selectors that find it are the main targets in up.fragment.config.mainTargets.
Separate selectors with a comma to update multiple fragments from a single request. Here opening a post also updates a bubble showing the number of unread posts:
<a href="/posts/5" up-target=".content, .unread-count">Read post</a> <!-- mark: .content, .unread-count -->
When one of your target elements is an ancestor of another target, Unpoly only updates the ancestor.
The following link would only update body, since that already contains .unread-count:
<a href="/home" up-target="body, .unread-count">...</a>
By default Unpoly expects all targeted fragments to be present in both the current page and the server response.
If a target selector is missing in the current page or in the response, an error up.CannotMatch is thrown.
Mark a target as optional with the :maybe pseudo-selector:
<a href="/posts/5" up-target=".content, .unread-count:maybe">Read post</a> <!-- mark: :maybe -->
Now only .content is required to match. If .unread-count is missing in the current page
or in the server response, Unpoly only updates .content without an error.
Instead of listing an optional element in every link's target, you can set an [up-hungry] attribute
on the element itself. A hungry element is updated whenever a response contains a matching element,
regardless of what was targeted:
<div class="unread-count" up-hungry>12</div> <!-- mark: up-hungry -->
<!-- Following this link will update .content, .unread-count:maybe -->
<a href="/posts/5" up-target=".content">Read post</a>
Common use cases for [up-hungry] are unread message counters or page-specific subnavigation.
Such elements often live in the application layout, outside of the fragment that is being targeted.
See Hungry elements for details.
Instead of swapping an entire fragment, you can append children to an existing fragment
with the :after pseudo-selector. In the same fashion, :before prepends the loaded content.
A practical example is a paginated list of items. Below the list is a link to load the next page.
With :after in the [up-target], the next items are appended to the existing list:
<ul class="tasks">
<li>Wash car</li>
<li>Purchase supplies</li>
<li>Fix tent</li>
</ul>
<a href="/tasks?page=2" class="next-page" up-target=".tasks:after, .next-page"> <!-- mark: .tasks:after -->
Load more tasks
</a>
The server is still expected to render an entire <ul class="tasks">, but only its <li> children
are used to extend the existing list. The second target, .next-page, replaces the link
with one pointing to the page after.
To keep the target element, but replace all of its child content, use the :content pseudo-selector:
<div class="card">...</div>
<a href="/cards/5" up-target=".card:content">Show card #5</a> <!-- mark: :content -->
The server is still expected to render an element matching .card, but only its child content is used.
The .card element itself keeps its attributes, event listeners and any state your scripts attached to it.
For more advanced strategies for keeping elements, see Keeping elements.
To make a server request without changing a fragment, target the :none selector:
<a href="/ping" up-target=":none">Ping server</a> <!-- mark: :none -->
To replace all visible elements of a layer, use the :layer selector.
It targets the layer's topmost swappable element:
<a href="/admin" up-follow up-target=":layer">Admin area</a> <!-- mark: :layer -->
The topmost swappable element is the first child of the layer's container element:
<body> element.{ target } that the overlay was opened with.Sometimes there are multiple components with the same selector on the page:
<div class="card">...</div>
<div class="card">...</div>
<div class="card">...</div>
While you can set [id] attributes to uniquely identify an element,
this is often not necessary. When an ambiguous selector like .card matches more than one element,
Unpoly prefers to match a fragment near the link or form that the user interacted with (the origin).
If there is no match in close proximity, Unpoly updates the first match in the layer.
When rendering programmatically, pass the interaction's origin element as { origin } option.
Below are examples of ambiguous selectors that Unpoly resolves by proximity to the origin. A more elaborate example is the Tasks list of the Unpoly Demo App.
Assume we have two links that replace .card:
<div class="card">
Card #1 preview
<a href="/cards/1" up-target=".card">Show full card #1</a>
</div>
<div class="card">
Card #2 preview
<a href="/cards/2" up-target=".card">Show full card #2</a> <!-- mark-line -->
</div>
When clicking "Show full card #2", Unpoly replaces the second card, since that is a matching ancestor of the followed link.
The origin can only be considered in the current page, not in the server response.
In the example above the server is expected to render a single .card element.
<div class="card">
<div class="card-text">Card #1 preview</div>
<a href="/cards/1" up-target=".card .card-text">Show full card #1</a>
</div>
<div class="card">
<div class="card-text">Card #2 preview</div>
<a href="/cards/2" up-target=".card .card-text">Show full card #2</a> <!-- mark-line -->
</div>
When clicking "Show full card #2", Unpoly replaces the .card-text within the second card.
Again the server is expected to render a single .card element, since the origin is
only known in the current page.
Instead of relying on proximity, your targets can use :origin to refer to the
interaction's origin element. The :origin placeholder is replaced with a target derived
from the origin element.
Below we see two links that will each update the <div> next to them.
This requires a rather verbose [up-target] attribute:
<a href="/tasks/1" up-target="a[href='/tasks/1'] + div">Show task 1</a> <!-- mark: a[href='/tasks/1'] + div -->
<div>Task 1 will appear here</div>
<a href="/tasks/2" up-target="a[href='/tasks/2'] + div">Show task 2</a> <!-- mark: a[href='/tasks/2'] + div -->
<div>Task 2 will appear here</div>
We can simplify the [up-target] by referencing the followed link as :origin:
<a href="/tasks/1" up-target=":origin + div">Show task 1</a> <!-- mark: :origin + div -->
<div>Task 1 will appear here</div>
<a href="/tasks/2" up-target=":origin + div">Show task 2</a> <!-- mark: :origin + div -->
<div>Task 2 will appear here</div>
When a link is clicked, :origin is replaced with a target derived from the link element.
For example, clicking on the second link will target a[href='/tasks/2'].
If matching fragments around the origin does not work for you, you can tell Unpoly to always use the first fragment matching the target selector:
{ match: 'first' } option to a function that matches a fragment.[up-match=first] attribute on a link or form.up.fragment.config.match = 'first' to disable region-aware matching for all functions and elements.
You can then opt in again with { match: 'region' } or [up-match=region].By default Unpoly requires targets to match in both the current page and the server response.
If no matching element is found in the current page or in the response, an error up.CannotMatch is thrown.
For individual fragments that may be missing, make the target optional with :maybe.
When the primary target may be missing, configure a fallback instead.
A fallback target is used when the primary target cannot be matched.
Pass it as an [up-fallback] attribute or { fallback } option:
up.render({ url: '/path', target: '.content', fallback: 'body' }) // mark: fallback: 'body'
If .content is missing in the current page or in the response, Unpoly updates the body element.
If neither the primary nor the fallback target can be matched, the render pass fails with an error up.CannotMatch.
It is often useful to render the main target when the primary target cannot be matched. Missing targets are often caused by the server rendering an error message, or an unexpected screen like a login form. Falling back to the main target shows that screen, instead of the link or form failing silently.
To fall back to the main target, set an empty [up-fallback] attribute or pass a { fallback: true } option:
up.render({ url: '/path', target: '.content', fallback: true }) // mark: fallback: true
Falling back to the main target is the default when navigating.
Links and forms navigate by default, so they don't need an [up-fallback] attribute.
When you pass an Element object to a rendering function, Unpoly derives
a selector that matches the element:
let element = document.querySelector('#foo')
up.reload(element) // Derives the target '#foo' from the given element
The element is also used as the origin of the update. See Target derivation for details and examples.
The server may elect to render a different target by setting an X-Up-Target response header.
Events like up:link:follow, up:form:submit and up:fragment:loaded also let you change the target
by setting event.renderOptions.target to a new selector.
See Changing options before rendering for an example.
Tip Your coding agent can search these docs locally with the Unpoly agent skill