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

# Polling

A fragment can reload itself from the server periodically. This picks up changes made by other users or by background jobs, using regular HTTP requests instead of a persistent connection.

> #### Tip
>
> When a delay of a few seconds is too much, your server can [signal changes as they happen](https://unpoly.com/learn/real-time-updates.md).

<nav aria-label="Contents">

#### Contents

- [Polling a fragment](#example)
- [Controlling the reload interval](#interval)
- [Reloading from another URL](#source)
- [The target selector is derived](#target)
- [Polling pauses in the background](#pausing)
- [Stopping polling](#stopping)
- [Handling error responses](#failed-responses)
- [Saving bandwidth when nothing changed](#detecting-unchanged-content)
- [Skipping updates on the client](#preventing-request)
- [Polling from JavaScript](#scripting)

</nav>

## Polling a fragment {#example}

The `.unread-count` fragment below shows the number of unread messages. Set an [`[up-poll]`](https://unpoly.com/api/up-poll.md) attribute to refresh it every 30 seconds:

```html
<div class="unread-count" up-poll>
  2 new messages
</div>
```

Every 30 seconds Unpoly requests the URL the fragment was loaded from, finds the `.unread-count` element in the response and swaps it into the page. The rest of the page is left alone. Reloading does not scroll, move focus or change the browser URL.

The response is expected to again contain an [`[up-poll]`](https://unpoly.com/api/up-poll.md) attribute on the fragment. This is how the server keeps polling going, or [stops it](#stopping).

A reload is a regular render pass: it [aborts](https://unpoly.com/learn/aborting-requests.md) earlier requests targeting the fragment or its descendants, and is itself aborted when the user updates the fragment or a container around it.

While the reload is loading, the fragment gets the [`.up-loading`](https://unpoly.com/learn/feedback-classes.md) class. To prevent this, set an [`[up-feedback=false]`](https://unpoly.com/api/up-poll.md#up-feedback) attribute on the fragment.

## Controlling the reload interval {#interval}

Set an `[up-interval]` attribute with a number of milliseconds:

```html
<div class="unread-count" up-poll up-interval="10000">
  2 new messages
</div>
```

Without the attribute, the global default of 30 seconds is used. You can change it by configuring [`up.radio.config.pollInterval`](https://unpoly.com/api/up.radio.config.md#config.pollInterval):

```js
up.radio.config.pollInterval = 10000
```

## Reloading from another URL {#source}

The fragment is reloaded from the URL it was originally loaded from. Unpoly remembers that URL in an [`[up-source]`](https://unpoly.com/api/up-source.md) attribute when the fragment is inserted.

To reload from a different URL, set the [`[up-source]`](https://unpoly.com/api/up-source.md) attribute yourself:

```html
<div class="unread-count" up-poll up-source="/unread-count">
  2 new messages
</div>
```

A dedicated route like `/unread-count` can render just the fragment, which is cheaper than rendering the full page it was first loaded with.

## The target selector is derived {#target}

Unpoly [derives](https://unpoly.com/learn/target-derivation.md) a target selector from the polling element, so it can find the element's counterpart in the response. The element below is polled with the selector `#score`:

```html
<div id="score" up-poll>
  Score: 1400
</div>
```

When you see a warning `Cannot poll untargetable fragment`, Unpoly could not derive a selector that identifies the element. Give the element a unique `[id]` or [`[up-id]`](https://unpoly.com/api/up-id.md) attribute.

## Polling pauses in the background {#pausing}

Polling pauses while the browser tab is hidden, and resumes when the user returns to the tab.

Polling also pauses while the fragment's [layer](https://unpoly.com/api/up.layer.md) is covered by an overlay, and resumes when the overlay closes. To keep polling under an overlay, set an [`[up-if-layer=any]`](https://unpoly.com/api/up-poll.md#up-if-layer) attribute.

When the user returns after at least one interval was spent in the background, Unpoly reloads the fragment immediately. You can use this to refresh a page when the user comes back after working on something else for a while. The following reloads your [main fragment](https://unpoly.com/api/main.md) after an absence of five minutes or more:

```html
<main up-poll up-interval="300_000">
  ...
</main>
```

## Stopping polling {#stopping}

Polling stops when any of the following happens:

- The fragment from the server response no longer has an [`[up-poll]`](https://unpoly.com/api/up-poll.md) attribute.
- The fragment from the server response has an `[up-poll="false"]` attribute.
- Your JavaScript calls [`up.radio.stopPolling()`](https://unpoly.com/api/up.radio.stopPolling.md) with the polling element.

The first two let the server end polling once the work is done. A fragment that shows the progress of a background job can poll until the job has finished:

```html
<div id="export-status" up-poll up-interval="2000">
  Exporting 43 of 120 records...
</div>
```

The response for the finished job renders the same element without [`[up-poll]`](https://unpoly.com/api/up-poll.md):

```html
<div id="export-status">
  <a href="/exports/5/download">Download your export</a>
</div>
```

## Handling error responses {#failed-responses}

By default polling will _not_ render responses with an error code, even when the response contains a matching fragment. The server is polled again after the configured interval.

To render _any_ response that contains a matching fragment, set an [`[up-fail=false]`](https://unpoly.com/api/up-poll.md#up-fail) attribute:

```html
<div class="download-status" up-poll up-fail="false">
  Download not ready yet.
</div>
```

This updates the fragment with any response containing a `.download-status` element, even when that response has a 4xx or 5xx status code.

When polling encounters a fatal error, like a timeout or a lost network connection, it tries again after the configured interval.

## Saving bandwidth when nothing changed {#detecting-unchanged-content}

Many polling requests find that nothing has changed. Your server can then skip rendering and respond with an empty `304 Not Modified`, which costs about 1 KB and no rendering time. Unpoly keeps polling at the configured interval.

See [Conditional requests](https://unpoly.com/learn/conditional-requests.md) for how to set this up with an [`ETag`](https://unpoly.com/api/ETag.md) or a modification time.

## Skipping updates on the client {#preventing-request}

Before each reload, an [`up:fragment:poll`](https://unpoly.com/api/up:fragment:poll.md) event is emitted on the polling fragment. Prevent the event to skip a single update:

```js
up.on('up:fragment:poll', function(event) {
  // Don't reload a fragment that contains a playing video
  let video = event.target.querySelector('video')
  if (video && !video.paused) {
    event.preventDefault()
  }
})
```

The server is polled again after the configured interval, emitting another [`up:fragment:poll`](https://unpoly.com/api/up:fragment:poll.md) event.

To discard a response that has already been received, use the [`up:fragment:loaded`](https://unpoly.com/api/up:fragment:loaded.md) event. To keep individual elements within the reloaded fragment, give them an [`[up-keep]`](https://unpoly.com/api/up-keep.md) attribute.

## Polling from JavaScript {#scripting}

To start polling an element that has no [`[up-poll]`](https://unpoly.com/api/up-poll.md) attribute, pass it to [`up.radio.startPolling()`](https://unpoly.com/api/up.radio.startPolling.md):

```js
let element = document.querySelector('.unread-count')
up.radio.startPolling(element, { interval: 10000 })
```

The options object may contain any option from [`[up-poll]`](https://unpoly.com/api/up-poll.md), like `{ url }` or `{ ifLayer }`.

To stop, pass the element to [`up.radio.stopPolling()`](https://unpoly.com/api/up.radio.stopPolling.md):

```js
up.radio.stopPolling(element)
```

<nav aria-label="Reading order">

- [Previous: Infinite scrolling](https://unpoly.com/learn/infinite-scrolling.md)
- [Next: Streaming real-time updates](https://unpoly.com/learn/real-time-updates.md)

</nav>

