---
area: "Learn"
parent: "Scripting chapter"
url: "https://unpoly.com/learn/templates"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Scripting chapter](https://unpoly.com/learn/scripting.md)</nav>

# Templates

By embedding template elements into your responses, your frontend can clone HTML fragments without making another server request.

While Unpoly apps render on the server primarily, client-side templates can be useful for [placeholders](https://unpoly.com/learn/placeholders.md), small [overlays](https://unpoly.com/learn/opening-overlays.md), or [optimistic rendering](https://unpoly.com/learn/optimistic-rendering.md).

<nav aria-label="Contents">

#### Contents

- [Rendering a template](#rendering-a-template)
- [Dynamic templates with variables](#dynamic)
- [Template lookup order](#lookup)

</nav>

## Rendering a template {#rendering-a-template}

Instead of [rendering an HTML string](https://unpoly.com/learn/providing-html.md#string), you may also refer to a [`<template>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template). This is useful for using the same HTML multiple times, or when you don't want to embed long HTML strings into attributes.

To refer to a template, pass its CSS selector to any attribute or option that accepts HTML:

```html
<a href="#" up-target=".target" up-document="#my-template">Click me</a>

<div class="target">
  Old content
</div>

<template id="my-template">
  <div class="target">
    New content
  </div>
</template>
```

When the link is clicked, Unpoly will look for a template matching `#my-template`. The template is cloned and the `.target` is updated with a matching element from the cloned template. All of this happens locally, without making a server request.

### Shorthand {#shorthand}

Since the target can be [derived](https://unpoly.com/learn/target-derivation.md) from the template's root element (`.target`), and since [we can omit `[href]`](https://unpoly.com/learn/providing-html.md#omitting-href), we can shorten the link to this:

```html
<a up-fragment="#my-template">Click me</a>
```

### Features that support templates {#features-that-support-templates}

Rendering of template selectors is supported by most attributes and functions that process HTML:

| JavaScript function | HTML attribute |
| --- | --- |
| [`up.render({ content: '#my-template' })`](https://unpoly.com/api/up.render.md) | `[up-content="#my-template"]` |
| [`up.render({ fragment: '#my-template' })`](https://unpoly.com/api/up.render.md) | `[up-fragment="#my-template"]` |
| [`up.render({ document: '#my-template' })`](https://unpoly.com/api/up.render.md) | `[up-document="#my-template"]` |
| [`up.render({ placeholder: '#my-template' })`](https://unpoly.com/api/up.render.md) | `[up-placeholder="#my-template"]` |
| [`up.Preview#insert()`](https://unpoly.com/api/up.Preview.prototype.insert.md) | – |
| [`up.Preview#swapContent()`](https://unpoly.com/api/up.Preview.prototype.swapContent.md) | – |
| [`up.Preview#showPlaceholder()`](https://unpoly.com/api/up.Preview.prototype.showPlaceholder.md) | – |

## Dynamic templates with variables {#dynamic}

Sometimes we want to clone a template with variations. For example, we may want to change a piece of text, or vary the size of a component.

As an example, we have a simple template that describes a single element. We want to use this template in various places, but set a custom element text every time:

```html
<template id="task-template">
  <div class="task">
    {{taskDescription}}
  </div>
</template>
```

There are multiple methods to implement template variables, including [compilers](#compiler-postprocessing), [template engines](#template-engine) and post-processing [callbacks](#callback-postprocessing).

> #### Note
>
> The variable syntax with the twin curly braces is only an example. You can implement templates with arbitrary grammar.

### _Option 1_ Processing compiler data {#compiler-postprocessing}

Templates can be cloned with a [data object](https://unpoly.com/learn/data.md), which is passed to any compiler function that compiles the cloned element. This lets you implement template variables without a [template engine](#template-engine).

When we reference a template, we can pass a data object for the cloned element as an [`[up-use-data]`](https://unpoly.com/api/up-follow.md#up-use-data) attribute:

```html
<a up-fragment="#task-template" up-use-data="{ description: 'Buy toast' }">
  Click me
</a>
```

By defining a matching [compiler](https://unpoly.com/learn/enhancing-elements.md) function, we can post-process the cloned element and its data object. When the compiler below is called with the cloned element, its [`data`](https://unpoly.com/learn/data.md) argument will be set to `{ description: 'Buy toast' }`:

```js
up.compiler('.task', function(task, data) {
  if (data.description) {
    task.innerText = data.description
  }
})
```

Sometimes we cannot use the `[up-use-data]` attribute, e.g. when configuring a [placeholder](https://unpoly.com/learn/placeholders.md). In that case we can append the data object after the template selector:

```html
<a href="/home" up-placeholder="#spinner { size: 'xl' }">
  Click me
</a>
```

### _Option 2_ Using a template engine {#template-engine}

Sometimes we want to render a complex data object:

```html
<a up-fragment="#results-template" up-use-data="
  {
    gameCount: 3,
    players: [
      { name: 'Luke', score: 100 },
      { name: 'Alice', score: 120 },
    ]
  }">
  Show results
</a>
```

For this we need a more expressive template with variables, loops or conditions.

There are [many template engines](https://awesome-javascript.js.org/resources/templating-engines.html) to choose from. For this example we're going to use templates in the style of [Mustache.js](https://github.com/janl/mustache.js) or [Handlebars](https://handlebarsjs.com/):

```html
<script id="results-template" type="text/mustache">
  <div id="game-results">
    <h1>Results of game {{gameCount}}</h1>

    {{#players}}
      <p>{{name}} has scored {{score}} points.</p>
    {{/players}}
  </div>
</script>
```

> #### Note
>
> We're using a `<script>` element here with a custom `[type]`, because strictly speaking `<template>` elements cannot have a `[type]` attribute. Both are valid methods to embed HTML fragments into a larger document, while largely being ignored by the browser.

#### Parsing template expressions {#parsing-template-expressions}

Unpoly does not ship with a template engine, but makes it very easy to implement or integrate your own:

- Listen to the [`up:template:clone`](https://unpoly.com/api/up:template:clone.md) event on your custom template elements.
- Process the given template and data using a templating function of your choice.
- Set `event.nodes` to a [list](https://unpoly.com/api/List.md) of [`Node`](https://developer.mozilla.org/en-US/docs/Web/API/Node) objects representing the template results.

#### Example integrations {#template-engine-example}

An event handler for a Mustache integration would look like this:

```js
up.on('up:template:clone', '[type="text/mustache"]', function(event) {
  const template = event.target.innerHTML
  const result = Mustache.render(template, event.data)
  event.nodes = up.element.createNodesFromHTML(result)
})
```

If you only care about simple `{{variable}}` replacements, you can also use JavaScript string functions, without adding a dependency. Here we define a very simple templating function for the `text/minimustache` type:

```js
up.on('up:template:clone', '[type="text/minimustache"]', function(event) {
  let template = event.target.innerHTML
  let evaluate = (_match, variable) => up.util.escapeHTML(event.data[variable])
  let filled = template.replace(/{{(\w+)}}/g, evaluate)
  event.nodes = up.element.createNodesFromHTML(filled)
})
```

### _Option 3_ Modifying the element programmatically {#callback-postprocessing}

You can use the [`[up-on-rendered]`](https://unpoly.com/api/up-follow.md#up-on-rendered) attribute to change the template element after it was cloned and inserted. The callback is provided with an [`up.RenderResult`](https://unpoly.com/api/up.RenderResult.md) which references the element we want to customize:

```html
<a
  up-fragment="#task-template"
  up-on-rendered="result.fragment.innerText = 'Buy toast'"
>
  Click me
</a>
```

From JavaScript you can [manually clone](https://unpoly.com/api/up.template.clone.md) a template and then modify the result:

```js
let [fragment] = up.template.clone('#task-template')
fragment.innerText = 'Buy Toast'
up.render({ fragment })
```

## Template lookup order {#lookup}

When matching a template with its CSS selector, Unpoly will start looking in the [origin](https://unpoly.com/api/origin.md) [layer](https://unpoly.com/api/up.layer.md) first. The origin is the element that caused an update. In the example above, this would be the clicked hyperlink.

If no template is found in the origin layer, Unpoly will look in ancestor layers, preferring closer layers. This allows you to declare global templates in your application layout (typically rendered into the [root layer](https://unpoly.com/api/up.layer.root.md)), but override them with more specific templates in overlays.

<nav aria-label="Reading order">

- [Previous: Attaching data to elements](https://unpoly.com/learn/data.md)
- [Next: Framework islands](https://unpoly.com/learn/islands.md)

</nav>

