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

# Placeholders

Placeholders are temporary spinners or UI skeletons shown within a fragment while it is loading. They appear instantly after the user's interaction and are replaced by the server response.

<figure>
<video src="https://unpoly.com/images/api/placeholders.webm" aria-label="Video: UI skeletons while screens are loading"></video>
<figcaption>

In a demo app, the user clicks "Projects" in the navigation bar. While the list is loading, gray bars in the shape of a heading, a button and table rows take the place of the content. Then the user clicks "New project". A modal overlay opens with the skeleton of a form, which the real form replaces when it has loaded.

</figcaption>
</figure>

By providing clues for how the page will ultimately look, placeholders make long-loading interactions feel more responsive.

<nav aria-label="Contents">

#### Contents

- [Basic placeholders](#basic-example)
- [Placeholders from templates](#from-template)
- [Placeholders for new overlays](#overlays)
- [Arbitrary placeholder logic](#from-preview)

</nav>

## Basic placeholders {#basic-example}

To show a placeholder while a link is loading, set an [`[up-placeholder]`](https://unpoly.com/api/up-placeholder.md) attribute with the placeholder's HTML as its value:

```html
<a href="/path" up-target="#target" up-placeholder="<p>Loading…</p>">Show story</a>

<div id="target">
  Old content
</div>
```

When the link is clicked, the [targeted](https://unpoly.com/learn/targeting-fragments.md) fragment's content is [hidden](https://unpoly.com/api/hidden.md). In its place the placeholder is inserted as the only visible child of `#target`:

```html
<div id="target">
  <up-wrapper hidden>Old content</up-wrapper>
  <p>Loading…</p>
</div>
```

When the response is received, `#target` is updated with new HTML from the server:

```html
<div id="target">
  New content from server
</div>
```

Like all [preview effects](https://unpoly.com/learn/previews.md), a placeholder only temporarily displaces a fragment. When the request [ends for any reason](https://unpoly.com/learn/previews.md#ending), the placeholder is removed and the original content is restored before the response is processed. This also happens when the server responds with an error, or when the request is aborted.

Placeholders are not shown when content is rendered from the cache. See [using previews with caching](https://unpoly.com/learn/previews.md#using-previews-with-caching).

Forms can show placeholders the same way, by setting [`[up-placeholder]`](https://unpoly.com/api/up-placeholder.md) on the `<form>` element. When [targeting multiple fragments](https://unpoly.com/learn/targeting-fragments.md#multiple), the placeholder is shown in the first fragment.

### From JavaScript {#from-javascript}

In your scripts you may pass a `{ placeholder }` option to most rendering functions:

```js
up.navigate({
  url: '/path',
  target: '#target',
  placeholder: '<p>Loading…</p>'
})
```

Instead of passing an HTML snippet you may also pass an [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element) or a [template reference](#from-template).

## Placeholders from templates {#from-template}

Instead of passing the placeholder HTML directly, you can refer to any [template](https://unpoly.com/learn/templates.md) by its CSS selector. This is useful when you don't want to set a long HTML string as an attribute value, or when you want to reuse the same placeholder many times:

```html
<a href="/path" up-target="#target" up-placeholder="#loading-template">Show story</a>

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

<template id="loading-template">
  <p>
    Loading…
  </p>
</template>
```

### Dynamic templates {#dynamic-templates}

You may need to customize placeholder markup to better fit the targeted fragment. For example, a spinner animation should be larger when updating a larger content area. Also when building a UI skeleton, its shapes must resemble the eventual screen state to be believable.

To limit the proliferation of template variants, you can make a few templates that are [customizable with variables](https://unpoly.com/learn/templates.md#dynamic). When referring to a template, append the variables as a data object after the template selector:

```html
<a
  href="/path"
  up-target="#target"
  up-placeholder="#loading-template { size: 'xl', message: 'Please wait' }">
  Show story
</a>
```

The template can then use the variables:

```html
<script id="loading-template" type="text/minimustache">
  <p class="{{size}}">
    {{message}}
  </p>
</script>
```

> #### Tip
>
> This example uses the minimal `text/minimustache` templating function that you can [copy into your project](https://unpoly.com/learn/templates.md#template-engine-example). See [Dynamic templates](https://unpoly.com/learn/templates.md#dynamic) for different strategies to build customizable templates.

## Placeholders for new overlays {#overlays}

You can use placeholders with [links that open an overlay](https://unpoly.com/api/up-layer-new.md):

```html
<a href="/path" up-follow up-layer="new" up-placeholder="<p>Loading…</p>">Open overlay</a>
```

This opens a temporary overlay with the same [visual style](https://unpoly.com/learn/customizing-overlays.md) and open animation as the link's overlay would have shown.

When the server response is received, the temporary overlay is closed and another overlay is opened with the response content. The close and open animations are disabled for this switch.

When the response ends up _not_ opening an overlay (e.g. the server responds with an [error code](https://unpoly.com/learn/error-responses.md)), the placeholder overlay is closed.

If the user dismisses the placeholder overlay before the server responds, the request is [aborted](https://unpoly.com/learn/aborting-requests.md).

## Arbitrary placeholder logic {#from-preview}

Sometimes you need more than [templates and variables](#dynamic-templates) can give you:

- Complex placeholder construction logic
- Showing a placeholder in a fragment that wasn't targeted
- Placeholders that affect multiple fragments

In such cases you can define a [preview](https://unpoly.com/learn/previews.md), which describes arbitrary loading state in JavaScript. A preview can show a placeholder in any element with [`up.Preview#showPlaceholder()`](https://unpoly.com/api/up.Preview.prototype.showPlaceholder.md). For example, this preview shows a loading message in a `#sidebar` element while the targeted fragment is loading:

```js
up.preview('sidebar-loading', function(preview) {
  preview.showPlaceholder('#sidebar', '<p>Loading…</p>')
})
```

The placeholder is removed when the preview ends, just like a placeholder set with [`[up-placeholder]`](https://unpoly.com/api/up-placeholder.md).

<nav aria-label="Reading order">

- [Previous: Feedback classes](https://unpoly.com/learn/feedback-classes.md)
- [Next: Previews](https://unpoly.com/learn/previews.md)

</nav>

