Unpoly 3.12.0 released

Posted . Visible to the public. Auto-destruct in 60 days

This release adds asynchronous compilers Show archive.org snapshot and many other features requested by the community.
We also fixed a number of performance regressions Show archive.org snapshot introduced by Unpoly 3.11 Show archive.org snapshot .

Breaking changes are marked with a ⚠️ emoji and polyfilled by unpoly-migrate.js Show archive.org snapshot .

Asynchronous compilers

Compiler functions can now be async Show archive.org snapshot . This is useful when a compiler needs to fetch network resources, or when calling a library with an asynchronous API:

up.compiler('textarea.wysiwyg', async function(textarea) {
  let editor = await import('wysiwyg-editor')
  editor.init(textarea)
})

You can also use this to split up expensive tasks, giving the browser a chance to render and process user input:

up.compiler('.element', async function(element) {
  doRenderBlockingWork(element)
  await scheduler.yield()
  doUserVisibleWork(element)
})

Cleaning up async work

Like synchronous compilers, async compiler functions can return a destructor function Show archive.org snapshot :

up.compiler('textarea.wysiwyg', async function(textarea) {
  let editor = await import('wysiwyg-editor')
  editor.init(textarea)
  return () => editor.destroy(textarea)
})

Unpoly guarantees that the destructor is called, even if the element gets destroyed before the compiler function terminates.

Timing render-blocking mutations

Unpoly will run the first task Show archive.org snapshot of every compiler function before allowing the browser to render DOM changes. If an async compiler function runs for multiple tasks, the browser will render between tasks. If you have render-blocking mutations that should be hidden from the user, these must happen in the first task.

Timing of compiler tasks and browser render frames

Async compilers will not delay the promise returned by rendering functions up.render() Show archive.org snapshot or up.layer.open() Show archive.org snapshot .
Async compilers will delay the promise returned by up.render().finished Show archive.org snapshot and up.hello() Show archive.org snapshot .

up.hello() Show archive.org snapshot is now async

⚠️ The up.hello() Show archive.org snapshot function now returns a promise that fulfills when all synchronous and asynchronous compilers have terminated:

let textarea = up.element.createFromHTML('<textarea class="wysiwyg"></textarea>')
await up.hello(textarea)
// WYISWYG editor is now initialized

The fulfillment value is the same element that was passed as an argument:

let html = '<textarea class="wysiwyg"></textarea>'
let textarea = await up.hello(up.element.createFromHTML(html))

Performance fixes

Unpoly 3.11 introduced a number of performance regressions that would be very noticable on pages with many elements, or many forms. To address this, this release includes a number of performance fixes:

  • Fix a performance regression where Unpoly would track the DOM for dynamically inserted [up-validate] Show archive.org snapshot fields for every form. Now fields are only tracked for forms that use [up-validate] Show archive.org snapshot .
  • Features that need to track the insertion or removal of elements now only sync with the DOM once after a render pass.
  • Watching a single field no longer tracks dynamically inserted fields.
  • Improved the performance of internal form lookups.

HTML content-type required

⚠️ Unpoly now requires server responses with an HTML content-type, like text/html or application/xhtml+xml. Trying to render responses with a different type will throw an error, even if the response body contains HTML markup.

Restricting content types is a security precaution. It protects in a hypothetical scenario where an attacker can both upload a file and can use an existing XSS vulnerability to cause Unpoly to render that file. It doesn't affect applications that reliably escape user input.

You can configure which responses Unpoly will process by configuring a function in up.fragment.config.renderableResponse Show archive.org snapshot . To render any response regardless of content-type, configure a function that always returns true`:

up.fragment.config.renderableResponse = (response) => true

New guides

The documentation Show archive.org snapshot has been extended with new guides:

Submit buttons can override form attributes

Submit buttons can now supplement or override most Unpoly attributes from the form:

<form method="post" action="/proposal/accept" up-submit>
  <button type="submit" up-target="#success">Accept</button>
  <button type="submit" up-target="#failure" up-confirm="Really reject?">Reject</button>
</form>

Individual submit buttons can now 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>
</form>

See [up-submit] Show archive.org snapshot for a list of overridable attributes

Sticky layout elements

When scrolling to reveal a target element, Unpoly will ensure that layout elements with [up-fixed=top] Show archive.org snapshot are not covering the revealed content.

You can now use [up-fixed] on elements with position: sticky Show archive.org snapshot . Unpoly will measure sticky element like permanently fixed elements. The current scroll position is not taken into account.

Support partial tables responses

In the past Unpoly didn't allow a server to optimize its response Show archive.org snapshot when the result was a single table row (or cell) without an enclosing <table>:

Content-type: text/html

<tr>
  <td>...</td>
</tr>

Unpoly can now parse responses that only contain a <tr>, <td> or <th> element, without an enclosing <table> (issue #91).

Expanding click areas

Unpoly lets you enlarge a link's click area using the [up-expand] Show archive.org snapshot attribute. This version addresses inconsistent (or impractical) assignment of the .up-active Show archive.org snapshot feedback class Show archive.org snapshot when an expanded link is clicked.

When either the [up-expand] Show archive.org snapshot container or the first link is clicked, the .up-active Show archive.org snapshot class
is now assigned to both elements:

<div up-expand class="up-active">
  <a href="/foo" class="up-active">Foo</a>
  <a href="/bar">Bar</a>
</div>

When a non-expanded link is clicked, now only that link becomes .up-active Show archive.org snapshot :

<div up-expand>
  <a href="/foo">Foo</a>
  <a href="/bar" class="up-active">Bar</a>
</div>

Preserving fragments

Two changes were made to preserving elements Show archive.org snapshot using the [up-keep] Show archive.org snapshot attribute:

Closing overlays

Manual booting

Fragment API

Smaller fixes and changes

Henning Koch
Last edit
Henning Koch
Attachments
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2025-09-24 13:30)