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

# Loading state

While Unpoly waits for the server, it can show loading state, like a spinner, a skeleton screen or even the expected result. Every effect appears instantly after the user's interaction and is reverted when the request ends. This makes your app feel responsive, even when the server takes a moment to answer.

<nav aria-label="Contents">

#### Contents

- [Styling loading elements](#styling-loading-elements)
- [Showing placeholders](#showing-placeholders)
- [Arbitrary temporary changes](#arbitrary-temporary-changes)
- [Optimistic rendering](#optimistic-rendering)
- [See also](#see-also)

</nav>

## Styling loading elements {#styling-loading-elements}

When the user clicks a link or submits a form, Unpoly adds an [`.up-active`](https://unpoly.com/api/up-active.md) class to that element, and an [`.up-loading`](https://unpoly.com/api/up-loading.md) class to the fragment it is targeting:

```html
<a href="/stories/5" up-target="#story" class="up-active">Read story</a>

<div id="story" class="up-loading">
  Old content
</div>
```

Style these classes in your CSS to show that the app is working:

```css
.up-active {
  outline: 2px solid blue;
}

.up-loading {
  opacity: 0.6;
}
```

Both classes are removed when the response arrives. To also disable a form's fields and buttons while it submits, see [Disabling forms while working](https://unpoly.com/learn/disabling-forms.md).

<nav aria-label="Read more">

- [More: Feedback classes](https://unpoly.com/learn/feedback-classes.md)

</nav>

## Showing placeholders {#showing-placeholders}

A placeholder is a spinner or UI skeleton shown within the targeted fragment while it is loading. Set the placeholder's HTML as an [`[up-placeholder]`](https://unpoly.com/api/up-placeholder.md) attribute:

```html
<a href="/stories/5" up-target="#story" up-placeholder="<p>Loading…</p>">Read story</a>
```

While the request is loading, the existing content of `#story` is hidden and the placeholder is shown in its place:

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

Placeholders can also be cloned from a [template](https://unpoly.com/learn/templates.md), so you can reuse the same skeleton for many links.

<nav aria-label="Read more">

- [More: Placeholders](https://unpoly.com/learn/placeholders.md)

</nav>

## Arbitrary temporary changes {#arbitrary-temporary-changes}

Feedback classes, placeholders and [disabling](https://unpoly.com/learn/disabling-forms.md) are _previews_: temporary page changes that Unpoly applies when a request starts, and reverts when the request ends. You can define your own preview with [`up.preview()`](https://unpoly.com/api/up.preview.md):

```js
up.preview('link-spinner', function(preview) {
  preview.insert(preview.origin, '<img src="spinner.gif">')
})
```

Links and forms refer to a preview by its name:

```html
<a href="/stories/5" up-follow up-preview="link-spinner">Read story</a>
```

When the user clicks, the spinner is appended to the link. When the request ends, the spinner is removed. This holds for any outcome: when the server responds with an error, when the request is aborted, or when the server ends up updating a different fragment. The page is always restored to a consistent state before the response is rendered.

<nav aria-label="Read more">

- [More: Previews](https://unpoly.com/learn/previews.md)

</nav>

## Optimistic rendering {#optimistic-rendering}

A preview can go beyond signaling and show the expected result before the server confirms it. This is called _optimistic rendering_. For example, a preview can read the submitted form data from `preview.params` and append a new item to a list:

```js
up.preview('add-task', function(preview) {
  let text = preview.params.get('text')
  let task = `<div class="task">${up.util.escapeHTML(text)}</div>`
  preview.insert('#tasks', task)
})
```

The item appears instantly. When the server responds, the optimistic change is reverted and replaced by the server-rendered list.

<nav aria-label="Read more">

- [More: Optimistic rendering](https://unpoly.com/learn/optimistic-rendering.md)

</nav>

## See also {#see-also}

When a request takes longer than 400 ms, Unpoly shows a thin progress bar at the top of the screen. To style, disable or replace it, see [Progress bar](https://unpoly.com/learn/progress-bar.md).

<nav aria-label="In this chapter">

## In this chapter {#children-index-title}

- [Feedback classes](https://unpoly.com/learn/feedback-classes.md)
- [Placeholders](https://unpoly.com/learn/placeholders.md)
- [Previews](https://unpoly.com/learn/previews.md)
- [Optimistic rendering](https://unpoly.com/learn/optimistic-rendering.md)
- [Progress bar](https://unpoly.com/learn/progress-bar.md)

</nav>

<nav aria-label="Reading order">

- [Previous: History in overlays](https://unpoly.com/learn/history-in-overlays.md)
- [Next: Feedback classes](https://unpoly.com/learn/feedback-classes.md)

</nav>

