---
area: "Learn"
parent: "Overlays chapter"
url: "https://unpoly.com/learn/targeting-other-layers"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Overlays chapter](https://unpoly.com/learn/overlays.md)</nav>

# Targeting other layers

Links, forms and JavaScript functions work within their own layer by default. An `[up-layer]` attribute or `{ layer }` option lets you address another layer instead, like the page behind an overlay or the root layer.

<nav aria-label="Contents">

#### Contents

- [Layer isolation](#isolation)
- [Updating another layer](#rendering)
- [Layer names](#layer-names)
- [The current layer](#current)
- [Trying multiple layers](#multiple)
- [Passing a layer object or element](#layer-object)
- [Looking up elements in another layer](#lookups)
- [Rendering error responses in another layer](#fail-layer)

</nav>

## Layer isolation {#isolation}

Layers are isolated from each other. Without an `[up-layer]` attribute, a link or form only updates its own layer.

Say an overlay and the page behind it both show a `.user-list`. This link inside the overlay updates the overlay's `.user-list`:

```html
<a href="/users" up-target=".user-list">Reload users</a>
```

The `.user-list` in the page behind the overlay stays unchanged, even though it matches the selector, too.

Lookup functions work the same way. [`up.fragment.get()`](https://unpoly.com/api/up.fragment.get.md) and [`up.fragment.all()`](https://unpoly.com/api/up.fragment.all.md) only see elements in the current layer:

```js
up.fragment.get('.user-list') // result: the .user-list in the current layer
```

## Updating another layer {#rendering}

A link inside an overlay updates that overlay. To update the layer behind it instead, set an `[up-layer]` attribute:

```html
<a href="/users" up-layer="parent" up-target=".user-list">Show all users</a>
```

Unpoly fetches `/users` and swaps the `.user-list` element in the parent layer. Because the overlay now obstructs the updated content, it is closed when the parent layer is updated. This is called [peeling](https://unpoly.com/learn/closing-overlays.md#peeling). To keep the overlay open, set an `[up-peel="false"]` attribute.

Forms work the same way. This form in an overlay creates a user, then updates the root layer:

```html
<form method="post" action="/users" up-layer="root" up-target=".user-list">
  ...
</form>
```

When rendering from JavaScript, pass a `{ layer }` option:

```js
up.render({ url: '/users', target: '.user-list', layer: 'parent' })
```

An `[up-layer]` attribute already implies [`[up-follow]`](https://unpoly.com/api/up-follow.md) or [`[up-submit]`](https://unpoly.com/api/up-submit.md), so you don't need to set both.

## Layer names {#layer-names}

The attribute or option accepts these names. Most are relative to the [current layer](#current-vs-front), which is usually the layer of the link or form:

| Name | Matches |
| --- | --- |
| `current` | The current layer. This is the default. |
| `parent` | The layer that opened the current layer |
| `root` | The root layer, which holds the initial page |
| `front` | The frontmost layer, which directly faces the user |
| `closest` | The current layer or any ancestor, preferring closer layers |
| `ancestor` | Any ancestor of the current layer, preferring closer layers |
| `child` | The overlay opened from the current layer |
| `descendant` | Any overlay opened from the current layer, directly or indirectly |
| `subtree` | The current layer and its descendants |
| `overlay` | Any overlay, preferring layers closer to the front |
| `any` | Any layer, preferring the current layer, then layers closer to the front |
| `origin` | The layer of the element passed as `{ origin }` |
| `0`, `1`, … | The layer at that index in the [layer stack](https://unpoly.com/api/up.layer.stack.md), `0` being the root layer |
| `new` | Opens a [new overlay](https://unpoly.com/learn/opening-overlays.md) instead of updating an existing layer |

Passing `any` disables [layer isolation](#isolation) for that call.

To replace existing overlays instead of stacking a new one on top, use `swap` or `shatter`. See [replacing existing overlays](https://unpoly.com/learn/opening-overlays.md#replacing-existing-overlays).

## The current layer {#current}

When no layer is given, Unpoly matches and renders in the layer the interaction started in:

- A link or form updates its own layer. Clicking an Unpoly-enabled link or submitting a form sets that element as the `{ origin }`, and the origin's layer is used.
- When an [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element) is passed as `{ target }` or `{ origin }` to a JavaScript function, that element's layer is used.
- Otherwise [`up.layer.current`](https://unpoly.com/api/up.layer.current.md) is used.

While a [compiler](https://unpoly.com/learn/enhancing-elements.md) runs, [`up.layer.current`](https://unpoly.com/api/up.layer.current.md) is the layer of the compiled element, even when that layer is in the background. This only holds for code that runs synchronously when the compiler is called. It does not hold for code that runs later:

- event listeners added by the compiler
- `setTimeout()` callbacks
- code after an `await`

In such code, [pass the element](#layer-object) as `{ layer }`.

### Current layer vs. frontmost layer {#current-vs-front}

Most of the time, the _current_ layer ([`up.layer.current`](https://unpoly.com/api/up.layer.current.md)) and the _frontmost_ layer ([`up.layer.front`](https://unpoly.com/api/up.layer.front.md)) are the same: the last layer in the stack. There are cases where the current layer is a layer in the background:

- While an element in a background layer is being [compiled](https://unpoly.com/learn/enhancing-elements.md).
- While an Unpoly event like [`up:request:loaded`](https://unpoly.com/api/up:request:loaded.md) is being emitted from a background layer.
- While an event listener bound to a background layer with [`up.Layer#on()`](https://unpoly.com/api/up.Layer.prototype.on.md) is being called.

In these cases `{ layer: 'current' }` and relative names like `parent` resolve from that background layer, which is what you usually want. To address the layer facing the user regardless, pass `{ layer: 'front' }`. To temporarily change the current layer in your own code, use [`up.Layer#asCurrent()`](https://unpoly.com/api/up.Layer.prototype.asCurrent.md).

## Trying multiple layers {#multiple}

You can list several layer names, separated by a space or comma. Unpoly tries them in the given order:

```html
<a href="/users" up-layer="parent, root" up-target=".user-list">Show all users</a>
```

Names that match no layer are skipped. Clicked in the root layer, the link above has no parent layer, so it updates the root layer.

When rendering, the target is sought in each listed layer, and the first layer containing it is updated. From JavaScript, you can also pass an array:

```js
up.render({ url: '/users', target: '.user-list', layer: ['parent', 'root'] })
```

## Passing a layer object or element {#layer-object}

Functions like [`up.layer.get()`](https://unpoly.com/api/up.layer.get.md) return an [`up.Layer`](https://unpoly.com/api/up.Layer.md) object, and [`up.layer.current`](https://unpoly.com/api/up.layer.current.md) or [`up.layer.root`](https://unpoly.com/api/up.layer.root.md) are such objects, too. Any of them can be passed as a `{ layer }` option.

You can also pass an [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element) as `{ layer }`. Unpoly then uses the layer that contains the element.

This is useful in async code, where the current layer may have changed by the time your callback runs. A compiler already has its element, so it can pass the element instead of remembering the layer:

```js
up.compiler('.user-list', function(element) {
  setTimeout(function() {
    // Ten seconds later, up.layer.current may be a different layer.
    let headline = up.fragment.get('h1', { layer: element })
    console.log('Headline is', headline)
  }, 10000)
})
```

## Looking up elements in another layer {#lookups}

Lookup functions like [`up.fragment.get()`](https://unpoly.com/api/up.fragment.get.md) and [`up.fragment.all()`](https://unpoly.com/api/up.fragment.all.md) only see the current layer by default. To match elsewhere, pass the same `{ layer }` option:

```js
up.fragment.get('.user-list', { layer: 'root' })
up.fragment.all('.flash', { layer: 'any' })
```

To get the [`up.Layer`](https://unpoly.com/api/up.Layer.md) object for a layer name, use [`up.layer.get()`](https://unpoly.com/api/up.layer.get.md). When a name matches several layers, [`up.layer.getAll()`](https://unpoly.com/api/up.layer.getAll.md) returns them all:

```js
up.layer.get('parent')      // result: the parent layer, or undefined on the root layer
up.layer.getAll('ancestor') // result: all ancestors, closest first
```

Other features that take a layer option include:

- A [navigation bar](https://unpoly.com/learn/navigation-bars.md#layers) with an `[up-layer]` attribute highlights links that point to another layer's location.
- An [`[up-hungry]`](https://unpoly.com/api/up-hungry.md) element with an `[up-if-layer]` attribute is updated by responses that were rendered into other layers.
- [`up.fragment.abort()`](https://unpoly.com/api/up.fragment.abort.md) can abort the requests of a given layer.

## Rendering error responses in another layer {#fail-layer}

Like most render options, `[up-layer]` only applies when the server responds successfully. When the server [responds with an error code](https://unpoly.com/learn/error-responses.md), like a form with validation errors, the response is rendered in the link's or form's own layer.

To render error responses in another layer, set an `[up-fail-layer]` attribute or pass a `{ failLayer }` option.

<nav aria-label="Reading order">

- [Previous: Customizing overlays](https://unpoly.com/learn/customizing-overlays.md)
- [Next: Layer context](https://unpoly.com/learn/layer-context.md)

</nav>

