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

# Feedback classes

Unpoly adds CSS classes to links and forms that are loading content, and to the fragments they are [targeting](https://unpoly.com/learn/targeting-fragments.md). The classes have no effect on their own. They exist so you can style them, to instantly signal that the app is working.

<nav aria-label="Contents">

#### Contents

- [Feedback when following links](#link)
- [Feedback when submitting forms](#form)
- [Conveying feedback with CSS styles](#styling)
- [Classes are removed when the request ends](#classes-are-removed-when-the-request-ends)
- [Feedback during cache revalidation](#cache-revalidation)
- [Feedback classes from JavaScript](#feedback-classes-from-javascript)
- [Custom feedback classes](#custom-classes)
- [Disabling feedback classes](#disabling-feedback-classes)

</nav>

## Feedback when following links {#link}

We have a link targeting a fragment `#target`:

```html
<a href="/path" up-target="#target">Link</a>

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

When the user clicks the link, the link is instantly assigned the [`.up-active`](https://unpoly.com/api/up-active.md) class. The [targeted](https://unpoly.com/learn/targeting-fragments.md) fragment (the `#target` element) gets the [`.up-loading`](https://unpoly.com/api/up-loading.md) class:

```html
<a href="/path" up-target="#target" class="up-active">Link</a>

<div id="target" class="up-loading">
  Initial content
</div>
```

Both classes are [removed](#classes-are-removed-when-the-request-ends) when the request ends.

## Feedback when submitting forms {#form}

We have a form targeting a fragment `#target`:

```html
<form action="/action" up-target="#target">
  <input type="text" name="email">
  <button type="submit">Submit</button>
</form>

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

When the user clicks the submit button, both the button and the form are marked as [`.up-active`](https://unpoly.com/api/up-active.md) while the form is submitting. The targeted fragment is marked as [`.up-loading`](https://unpoly.com/api/up-loading.md):

```html
<form action="/action" up-target="#target" class="up-active">
  <input type="text" name="email">
  <button type="submit" class="up-active">Submit</button>
</form>

<div id="target" class="up-loading">
  Initial content
</div>
```

> #### Tip
>
> To also prevent the user from interacting with the form while it submits, see [Disabling forms while working](https://unpoly.com/learn/disabling-forms.md).

### Fields can be active origins {#fields-can-be-active-origins}

Sometimes a form is not submitted by pressing a submit button, but by interacting with an input field:

- The user submits by pressing `Return` inside a focused field.
- A field with [`[up-autosubmit]`](https://unpoly.com/api/up-autosubmit.md) is changed.

In these cases, that field is considered the [origin](https://unpoly.com/api/origin.md) element that caused the submission. It is also marked as [`.up-active`](https://unpoly.com/api/up-active.md), in addition to the form and its default submit button:

```html
<form action="/action" up-submit class="up-active">
  <input type="text" name="email" class="up-active">
  <button type="submit" class="up-active">Submit</button>
</form>
```

## Conveying feedback with CSS styles {#styling}

To instantly signal that a user interaction has been registered, consider highlighting active links and submit buttons in your CSS:

```css
.up-active:is(a, [up-follow], input[type=submit], button[type=submit], button:not([type])) {
  outline: 2px solid blue;
}
```

To convey which parts of the page have a pending update, consider styling targeted fragments:

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

You can show a hidden loading message while a form is submitting:

```css
.spinner {
  display: none;

  form.up-active & {
    display: block;
  }
}
```

If CSS alone is not enough, [previews](https://unpoly.com/learn/previews.md) let you make arbitrary changes to the page while a request is loading. See [Loading state](https://unpoly.com/learn/loading-state.md) for all strategies.

## Classes are removed when the request ends {#classes-are-removed-when-the-request-ends}

Feedback classes remain set while the request is loading. Once the response is received, the [`.up-active`](https://unpoly.com/api/up-active.md) and [`.up-loading`](https://unpoly.com/api/up-loading.md) classes are removed:

```html
<a href="/path" up-target="#target">Link</a>

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

Feedback classes are also removed if the request ends for any other reason, such as the [server rendering an error code](https://unpoly.com/learn/error-responses.md#fail-options) or the request being [aborted](https://unpoly.com/learn/aborting-requests.md). See [How previews end](https://unpoly.com/learn/previews.md#ending) for all reasons that end a request.

## Feedback during cache revalidation {#cache-revalidation}

When content is rendered from the [cache](https://unpoly.com/learn/caching.md), no [`.up-active`](https://unpoly.com/api/up-active.md) or [`.up-loading`](https://unpoly.com/api/up-loading.md) classes are set, since there is nothing to wait for.

When the cache entry is stale, Unpoly [automatically reloads the fragment](https://unpoly.com/learn/caching.md#revalidation). During this cache revalidation Unpoly marks the targeted fragment as [`.up-revalidating`](https://unpoly.com/api/up-revalidating.md) instead of [`.up-loading`](https://unpoly.com/api/up-loading.md):

```html
<div id="target" class="up-revalidating">
  Possibly stale content
</div>
```

## Feedback classes from JavaScript {#feedback-classes-from-javascript}

When rendering from JavaScript, Unpoly sets [`.up-active`](https://unpoly.com/api/up-active.md) on the [origin](https://unpoly.com/api/origin.md) element that caused the interaction. For example, when following a link with [`up.follow()`](https://unpoly.com/api/up.follow.md), that link is [`.up-active`](https://unpoly.com/api/up-active.md) while loading:

```js
up.follow(link)
```

In situations where Unpoly cannot guess the origin, you can pass it as an `{ origin }` option. For example, when a button click causes a render pass, that button should be the origin:

```js
up.on('click', '.my-button', function(event) {
  up.render({ url: '/path', origin: event.target })
})
```

## Custom feedback classes {#custom-classes}

To set additional classes on activated [origins](https://unpoly.com/api/origin.md), push a class name to [`up.status.config.activeClasses`](https://unpoly.com/api/up.status.config.md#config.activeClasses):

```js
up.status.config.activeClasses.push('activated')
```

To set additional classes on targeted fragments that are loading new content, push a class name to [`up.status.config.loadingClasses`](https://unpoly.com/api/up.status.config.md#config.loadingClasses):

```js
up.status.config.loadingClasses.push('fetching')
```

To set additional classes during cache revalidation, push a class name to [`up.status.config.revalidatingClasses`](https://unpoly.com/api/up.status.config.md#config.revalidatingClasses):

```js
up.status.config.revalidatingClasses.push('checking')
```

## Disabling feedback classes {#disabling-feedback-classes}

To disable feedback classes, set an [`[up-feedback=false]`](https://unpoly.com/api/up-follow.md#up-feedback) attribute:

```html
<a href="/path" up-follow up-feedback="false">Link</a>
```

From JavaScript you may pass a [`{ feedback: false }`](https://unpoly.com/api/up.render.md#options.feedback) option:

```js
up.follow(link, { feedback: false })
```

<nav aria-label="Reading order">

- [Previous: Loading state](https://unpoly.com/learn/loading-state.md)
- [Next: Placeholders](https://unpoly.com/learn/placeholders.md)

</nav>

