Learn Providing HTML to render

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 }

Loading HTML from the server

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.

Usage in forms

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.

Programmatic API

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' })

Rendering a string of HTML

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.

Replacing a fragment's children

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.

Rendering a string that only contains the fragment

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 &lt; and &gt;.

Extracting a fragment from a document

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.

Omitting [href] for local updates

When 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;
}

Sanitizing user input

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.

Rendering a <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.

Rendering an Element object

Instead 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.

Rendering an up.Response object

You 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.Response is useful when accessing the discarded response of an overlay that was closed by the server.