Every render pass needs HTML to insert. Usually Unpoly loads it from a URL,
but you can also render HTML you already have: a string, an Element, a <template> or an up.Response.
Each source has its own attribute or option:
| Effect | HTML attribute | JavaScript option |
|---|---|---|
| Fetch a URL and update targeted fragments |
[href] or [action]
|
{ url } |
Update a fragment's inner HTML from a string, <template>, or Element
|
[up-content] |
{ content } |
Update a fragment's outer HTML from a string, <template>, or Element
|
[up-fragment] |
{ fragment } |
| Update targeted fragments from a larger HTML string | [up-document] |
{ document } |
Update targeted fragments from an existing up.Response object |
– | { response } |
To load an HTML document from the server when a link is clicked, set the URL as a standard [href] attribute.
Also set an [up-target] attribute indicating which fragment to update:
<a href="/path" up-target=".target">Click me</a> <!-- mark: up-target=".target" -->
<div class="target">
Content will appear here
</div>
The server is expected to respond with HTML that contains an element matching the target selector (.target):
<html>
...
<div class="target"> <!-- mark-line -->
New content <!-- mark-line -->
</div> <!-- mark-line -->
...
</html>
The response may contain other HTML, but only the element matching .target is extracted and placed into the page.
Apart from hungry elements, other elements in the response are discarded, and the corresponding elements on the page stay unchanged.
Tip
See targeting fragments for ways of controlling how the new fragment is inserted. For instance, you can choose to append the fragment instead of swapping it.
For a form, set the server endpoint URL as a standard [action] attribute.
Also set an [up-target] attribute indicating which fragment to update after a successful submission:
<form action="/path" up-target=".target"> <!-- mark: up-target=".target" -->
...
</form>
<div class="target">
Content will appear here
</div>
When the form is submitted, the server is expected to respond with HTML that contains an element matching .target.
If the server responds with an error code, Unpoly ignores the [up-target] attribute
and updates the selector in the [up-fail-target] attribute instead. The default fail target
is the form itself. See Handling error responses for details.
See Submitting forms in-place for an expanded example.
To render remote content from JavaScript, pass a { url } option to the up.render() function:
up.render({ url: '/path', target: '.target' })
When the fragment change represents a navigation, use up.navigate() instead.
It applies the navigation defaults, which update the browser location, scroll position and focus
when the main fragment changes:
up.navigate({ url: '/path', target: '.target' })
Sometimes you already have the HTML to render, as a JavaScript string or in an HTML attribute. You can render such a string without a server request.
To replace only an element's children, pass the new inner HTML
as an [up-content] attribute or { content } option.
For example, take the following HTML:
<div class="target">
Old content
</div>
We can update the element's children from a link like this:
<a href="#" up-target=".target" up-content="New content">Click me</a> <!-- mark: up-content="New content" -->
Clicking the link changes the targeted element's inner HTML to the attribute value:
<div class="target">
New content <!-- mark-line -->
</div>
Only the new inner HTML is inserted and compiled. The element node itself, including its attributes and event listeners, stays unchanged.
To render a string of HTML comprising only the new fragment's outer HTML,
pass it as a { fragment } option:
// This will update .target
up.render({ fragment: '<div class="target">New content</div>' }) // mark: fragment: '<div class="target">New content</div>'
Note how we omitted a { target } option.
The target is derived from the root element in the given HTML, yielding .target in the example above.
In HTML, embed the fragment in an [up-fragment] attribute:
<a href="#" up-fragment='<div class="target">New content</div>'>Click me</a> <!-- mark: up-fragment='<div class="target">New content</div>' -->
Tip
HTML5 allows unescaped angle brackets in quoted attribute values. It's also valid to escape them with
<and>.
Sometimes you have a larger string of HTML from which you want to update one or more elements:
let html = `
<main>
<div class="foo">...</div>
<div class="bar">...</div>
<div class="bar">...</div>
<div class="qux">...</div>
</main>
`
You can pass this HTML string as a { document } option. Also pass a { target } option indicating
which fragments to update:
up.render({ target: '.foo, .bar', document: html })
Only the targeted elements are extracted and placed into the page. Other elements parsed from the document string are discarded.
In HTML, the same option is available as an [up-document] attribute.
[href] for local updatesWhen updating fragments from a string or <template>, you may omit the [href="#"] attribute:
<a up-target=".target" up-content="New content">Click me</a> <!-- mark: up-content="New content" -->
Unpoly makes sure that such a link is focusable and supports keyboard activation.
Most browsers only tint and underline an a[href] element. You may need to update your CSS to also style links without an [href] attribute:
a:is([href], [up-content], [up-fragment], [up-document]) {
color: blue;
text-decoration: underline;
}
HTML provided via { content }, { fragment } or { document } is placed into the document without sanitization.
When dealing with user-controlled input, you must escape or sanitize it before rendering.
Also wrap it in an HTML <tag> to make sure it is interpreted as HTML, and not as a <template> selector:
let userText = document.querySelector('wysiwyg-textarea').value
up.render({ fragment: '<div class="foo">' + up.util.escapeHTML(userText) + '</div>' })
We also recommend a Content Security Policy.
<template>
Instead of passing an HTML string, you may also refer to a <template> element
in any attribute or option that accepts HTML:
<a href="#" up-target=".target" up-document="#my-template">Click me</a> <!-- mark: up-document="#my-template" -->
<div class="target">
Old content
</div>
<template id="my-template"> <!-- mark: id="my-template" -->
<div class="target">
New content
</div>
</template>
The template is cloned and the .target is updated with a matching element from the clone.
See Templates for many more examples, including ways to define dynamic templates with variables, loops or conditions.
Element objectInstead of a string you can also pass an Element value
as a { content }, { fragment } or { document } option:
let element = document.createElement('div')
element.textContent = 'New content'
up.render({ target: '.target', content: element })
If the element was already attached before rendering, it is moved to the target position in the DOM tree.
The element is compiled after insertion. If the element has been compiled before, it is not re-compiled.
If the element is a template, it is cloned before insertion.
up.Response objectYou can pass an up.Response object as a { response } option:
let response = await up.request('/path')
up.render({ target: '.target', response })
In addition to matching .target in the response text, the response headers are processed.
For example, when the response was sent with an X-Up-Target header, that header changes the rendered selector.
The up:fragment:loaded event is emitted as if the response had been loaded by the render pass itself.
Tip
Rendering an
up.Responseis useful when accessing the discarded response of an overlay that was closed by the server.
Tip Your coding agent can search these docs locally with the Unpoly agent skill