Learn Scripting

Unpoly pairs HTML elements with JavaScript behavior. Instead of running scripts once per page load, you register compilers that enhance matching elements whenever they enter the page: at the initial load, and again with every fragment update.

Enhancing elements

To enhance matching elements with JavaScript, register a compiler function with up.compiler():

up.compiler('.current-time', function(element) {
  element.textContent = new Date().toString()
})

The function is called once for each .current-time element, when the page first loads and when a matching fragment is rendered later. This replaces listening to DOMContentLoaded, which only fires for the initial page.

A compiler can return a destructor function. Unpoly calls it when it removes the element, e.g. when the surrounding fragment is swapped. This keeps effects like timers or global listeners from outliving their element.

Attaching data to elements

The server can attach structured data to an element, as relaxed JSON in an [up-data] attribute:

<span class="user" up-data="{ age: 31, name: 'Alice' }">Alice</span> <!-- mark: up-data -->

The parsed data is passed to your compiler as a second argument:

up.compiler('.user', function(element, data) { // mark: data
  console.log(data.name) // result: "Alice"
})

Rendering templates

The server can embed <template> elements into its responses, for the frontend to clone without another request. This enables instant UI like placeholders or optimistic rendering:

<a up-fragment="#tip-template">Show tip</a> <!-- mark: up-fragment -->

<div class="tip">Click for a tip.</div>

<template id="tip-template"> <!-- mark: id="tip-template" -->
  <div class="tip">Press <kbd>?</kbd> to see all shortcuts.</div>
</template>

Clicking the link clones the template and swaps it into the matching .tip element, entirely on the client. Templates can also take variables.

Mounting framework components

A compiler can mount a component from a frontend framework like React or Vue, as an island within a server-rendered page:

up.compiler('.color-picker', function(element, data) {
  let root = createRoot(element)
  root.render(<ColorPicker value={data.value}/>)
  return () => root.unmount() // mark: return
})

The component mounts when its element enters the page, receives props from server data, and unmounts when the fragment around it is swapped.

See also

To convert existing DOMContentLoaded scripts to compilers, see Migrating legacy JavaScripts.

To control how scripts and callbacks run under a Content Security Policy, see Script security.