---
area: "Learn"
parent: "Network & caching chapter"
url: "https://unpoly.com/learn/aborting-requests"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Network & caching chapter](https://unpoly.com/learn/network.md)</nav>

# Aborting requests

When two requests [target](https://unpoly.com/learn/targeting-fragments.md) the same element, Unpoly aborts the earlier request. Only the later response is rendered, regardless of the order in which responses arrive. This prevents non-deterministic updates when two requests race to render the same fragment.

<nav aria-label="Contents">

#### Contents

- [Conflicting requests are aborted](#aborting-conflicting-requests)
- [Controlling what is aborted](#controlling)
- [Protecting a request from being aborted](#preventing)
- [Aborting from JavaScript](#aborting-imperatively)
- [Reacting to aborted requests](#reacting)
- [Aborting rules in layers](#layers)
- [Destroyed fragments are aborted](#destroyed)

</nav>

## Conflicting requests are aborted {#aborting-conflicting-requests}

By default, a render pass aborts earlier requests targeting fragments within _its own_ targeted fragments. Requests targeting other fragments are not aborted.

To visualize this, consider a layout with a sidebar (`#side`) and a content area (`#main`). Contained within `#main` is a smaller fragment `#box`:

<figure>
<img src="https://unpoly.com/images/api/side-main-box.svg" alt="Diagram of a layout with three fragments">
<figcaption>

The sidebar `#side` is on the left and the content area `#main` on the right, side by side. The smaller `#box` sits inside `#main`.

</figcaption>
</figure>

In this layout, rendering has the following effects:

- Concurrent requests targeting `#side` and `#main` do not abort each other.
- When two requests target `#main`, the first request is aborted by the second request.
- Rendering `#main` aborts an earlier request targeting `#box`.
- Rendering `#box` does _not_ abort an earlier request targeting `#main`.

Other work waiting on a fragment stops as well when the fragment is aborted: [polling](https://unpoly.com/learn/polling.md) ends, and pending [validations](https://unpoly.com/learn/validation.md) are dropped.

## Controlling what is aborted {#controlling}

To control which requests are aborted, pass an [`{ abort }`](https://unpoly.com/api/up.render.md#options.abort) option to rendering functions like [`up.follow()`](https://unpoly.com/api/up.follow.md), [`up.submit()`](https://unpoly.com/api/up.submit.md) or [`up.render()`](https://unpoly.com/api/up.render.md). In your HTML, set an `[up-abort]` attribute with the same values:

```html
<a href="/dashboard" up-follow up-abort="layer">Dashboard</a>
```

| Value | Effect |
| --- | --- |
| `'target'` | Aborts earlier requests targeting fragments within _your_ targeted fragments. This is the default. |
| `'layer'` | Aborts all requests targeting a fragment on the same [layer](https://unpoly.com/api/up.layer.md) as you. |
| `'all'` | Aborts all requests targeting any fragment on any layer. |
| `false` | Aborts nothing. |

With `{ abort: false }`, two requests targeting the same fragment are rendered in the order of their responses. If the first response removes the fragment targeted by the second request, the second request fails or updates a [fallback target](https://unpoly.com/api/up.render.md#options.fallback).

## Protecting a request from being aborted {#preventing}

To protect a request from being aborted through `{ abort }`, pass an [`{ abortable: false }`](https://unpoly.com/api/up.render.md#options.abortable) option or set an `[up-abortable="false"]` attribute:

```html
<form method="post" action="/orders" up-submit up-abortable="false">
  ...
</form>
```

Submitting this form is not interrupted when the user navigates to another page while the request is loading.

An aborted request may already have reached your server. For a form that changes data, aborting only means that its response is not rendered.

[Preload](https://unpoly.com/learn/preloading.md) requests are not abortable by default, so navigating does not cancel a menu that is still preloading in the background.

## Aborting from JavaScript {#aborting-imperatively}

To abort requests targeting a fragment, pass the element or a selector to [`up.fragment.abort()`](https://unpoly.com/api/up.fragment.abort.md):

```js
up.fragment.abort('.content')
```

To abort all requests targeting a [layer](https://unpoly.com/api/up.layer.md), pass a `{ layer }` option:

```js
up.fragment.abort({ layer: 'root' })
up.fragment.abort({ layer: 'any' }) // aborts requests on all layers
```

There is also a low-level [`up.network.abort()`](https://unpoly.com/api/up.network.abort.md) function, which aborts requests matching a [URL pattern](https://unpoly.com/api/url-patterns.md) or an arbitrary condition. Prefer [`up.fragment.abort()`](https://unpoly.com/api/up.fragment.abort.md) when you can: only requests aborted by screen region let components [react to being aborted](#reacting).

## Reacting to aborted requests {#reacting}

When requests are aborted through `{ abort }` or [`up.fragment.abort()`](https://unpoly.com/api/up.fragment.abort.md), an [`up:fragment:aborted`](https://unpoly.com/api/up:fragment:aborted.md) event is emitted on the element for which requests were aborted, and an [`up:request:aborted`](https://unpoly.com/api/up:request:aborted.md) event for each aborted request.

A rendering function whose request was aborted rejects with an `up.Aborted` error. Its `{ name }` is `'AbortError'`, like the `DOMException` that `fetch()` and other native APIs reject with when aborted. To detect both kinds of aborts, check the error's name:

```js
try {
  await up.render({ url: '/path', target: '.content' })
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Rendering was aborted: ' + error.message)
  }
}
```

To run code when an element _or one of its ancestors in the same layer_ was aborted, use [`up.fragment.onAborted()`](https://unpoly.com/api/up.fragment.onAborted.md). For example, a timer that reloads an element after 10 seconds should not fire once the element's requests were aborted:

```js
let timeout = setTimeout(() => up.reload(element), 10000)
up.fragment.onAborted(element, () => clearTimeout(timeout))
```

The callback is unsubscribed automatically when the element is destroyed.

## Aborting rules in layers {#layers}

The following rules apply when opening or closing [overlays](https://unpoly.com/api/up.layer.md):

- A request to open a new overlay will abort existing requests targeting the base layer's [main fragment](https://unpoly.com/api/main.md) or its descendants.
- When two requests attempt to [open a new overlay](https://unpoly.com/api/up-layer-new.md) over the same base layer, the first request will be aborted by the second request.
- A request to open a new overlay is aborted when the base layer's [main fragment](https://unpoly.com/api/main.md) is targeted by a second request.
- When a layer is closed, all pending requests targeting that layer are aborted. This is regardless of the `{ abort }` option used.
- Aborting a layer doesn't affect other layers. For example, [`up.fragment.abort({ layer: 'root' })`](https://unpoly.com/api/up.fragment.abort.md) keeps the requests, [polling](https://unpoly.com/api/up-poll.md) and pending [validations](https://unpoly.com/learn/validation.md) in an overlay.

## Destroyed fragments are aborted {#destroyed}

When a fragment is removed from the DOM with [`up.destroy()`](https://unpoly.com/api/up.destroy.md), any request targeting this fragment (or its descendants) is aborted. This is regardless of the `{ abort }` option used.

A render pass that replaces a fragment also aborts requests targeting the removed elements, unless it was given `{ abort: false }`.

<nav aria-label="Reading order">

- [Previous: Caching](https://unpoly.com/learn/caching.md)
- [Next: Background requests](https://unpoly.com/learn/background-requests.md)

</nav>

