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

# Opening overlays

Instead of updating the current page, any link or form can open content in a new overlay, like a modal or a drawer. The linked screen stays a regular page on your server, and the page behind the overlay keeps its state.

<nav aria-label="Contents">

#### Contents

- [Opening an overlay from a link](#link)
- [Opening an overlay from a form](#form)
- [Opening overlays from local content](#string)
- [Opening overlays from JavaScript](#scripted)
- [Opening overlays from the server](#server)
- [Replacing existing overlays](#replacing-existing-overlays)

</nav>

## Opening an overlay from a link {#link}

To open a link's destination in a modal overlay, set an [`[up-layer=new]`](https://unpoly.com/api/up-layer-new.md) attribute:

```html
<a href="/menu" up-layer="new">Open menu</a>
```

When the user clicks, Unpoly requests `/menu` and shows the response's [main fragment](https://unpoly.com/api/main.md) in the overlay. Your server renders the same full page it would for a regular page load, so the link keeps working in a new tab or without JavaScript.

### Choosing what the overlay shows {#content}

Often the page's main fragment works well in an overlay as it is. When it doesn't, use the first of these options that fits:

1. Mark a different [main fragment](https://unpoly.com/api/up-main.md#overlays) for overlays. Set `[up-main=overlay]` for all overlays, or name a [mode](#modes) like `[up-main=modal]`:

   ```html
   <body>
     <div class="layout" up-main="root">
       <nav class="layout--side">...</nav>
       <div class="layout--content" up-main="overlay">
         ...
       </div>
     </div>
   </body>
   ```

   The root layer keeps updating the whole `.layout`, while overlays only show `.layout--content`.

2. Choose a fragment for a single link with an [`[up-target]`](https://unpoly.com/api/up-layer-new.md#up-target) attribute:

   ```html
   <a href="/menu" up-layer="new" up-target=".menu-items">Open menu</a>
   ```

3. Render something different on the server. Requests for an overlay carry an [`X-Up-Mode`](https://unpoly.com/api/X-Up-Mode.md) header with the overlay's mode.

When the server responds with an [error code](https://unpoly.com/learn/error-responses.md), the error page is rendered in the link's own layer, not in an overlay. To show errors in an overlay, set [`[up-fail-layer=new]`](https://unpoly.com/learn/targeting-other-layers.md#fail-layer).

When the user clicks a second overlay link while the first is still loading, only the second overlay opens. See [aborting rules in layers](https://unpoly.com/learn/aborting-requests.md#layers).

### Choosing the overlay mode {#modes}

By default the overlay opens as a `modal`. To use a different [mode](https://unpoly.com/learn/overlays.md#layer-modes), like a `popup` or `drawer`, set an `[up-mode]` attribute:

```html
<a href="/menu" up-layer="new" up-mode="drawer">Open menu</a>
```

As a shorthand, you can append the mode to the [`[up-layer=new]`](https://unpoly.com/api/up-layer-new.md) attribute:

```html
<a href="/menu" up-layer="new drawer">Open menu</a>
```

These modes are available:

| Mode | Description |
| --- | --- |
| `modal` | A box centered over a dimmed backdrop |
| `drawer` | A drawer sliding in from the side |
| `popup` | A popup menu anchored to a link |
| `cover` | An overlay covering the entire screen |

You can change the default mode in [`up.layer.config.mode`](https://unpoly.com/api/up.layer.config.md#config.mode).

## Opening an overlay from a form {#form}

To show the response to a successful form submission in an overlay, set an [`[up-layer=new]`](https://unpoly.com/api/up-layer-new.md) attribute on the form:

```html
<form method="post" action="/subscribers" up-layer="new">
  ...
</form>
```

To only use an overlay for error messages from a [failed submission](https://unpoly.com/learn/error-responses.md), set an `[up-fail-layer=new]` attribute instead:

```html
<form method="post" action="/subscribers" up-fail-layer="new">
  ...
</form>
```

## Opening overlays from local content {#string}

The [`[up-content]`](https://unpoly.com/api/up-follow.md#up-content) attribute opens an overlay without going through the server:

```html
<a up-layer="new popup" up-content="<p>Helpful instructions here</p>">
  Help
</a>
```

Since no `[up-target]` selector is given, the content is wrapped in a container matching the overlay's [main target](https://unpoly.com/api/main.md).

Instead of embedding an HTML string, you can also refer to a [template](https://unpoly.com/learn/templates.md):

```html
<a up-layer="new popup" up-content="#help">
  Help
</a>

<template id="help">
  <p>Helpful instructions here</p>
</template>
```

## Opening overlays from JavaScript {#scripted}

When rendering from JavaScript, pass `{ layer: 'new' }` to open the fragment in a new overlay:

```js
up.navigate({ url: '/users/new', layer: 'new' })
```

Instead of [`up.navigate()`](https://unpoly.com/api/up.navigate.md) or [`up.render()`](https://unpoly.com/api/up.render.md) you can also call [`up.layer.open()`](https://unpoly.com/api/up.layer.open.md). It returns a promise for the new [`up.Layer`](https://unpoly.com/api/up.Layer.md) instance:

```js
let layer = await up.layer.open({ url: '/users/new' })
```

Choose a different layer mode by passing a `{ mode }` option:

```js
up.layer.open({ url: '/menu', mode: 'drawer' })
```

To get a promise for the overlay's [result value](https://unpoly.com/learn/subinteractions.md#result-values) instead, use [`up.layer.ask()`](https://unpoly.com/api/up.layer.ask.md):

```js
let address = await up.layer.ask({ url: '/address-picker' })
```

The promise fulfills when the overlay is accepted, and rejects when it is dismissed. See [Subinteractions: Awaiting subinteractions from JavaScript](https://unpoly.com/learn/subinteractions.md#awaiting-subinteractions-from-javascript).

## Opening overlays from the server {#server}

The server can force its response to open an overlay by sending an `X-Up-Open-Layer: {}` response header.

This opens an overlay with the [default mode](https://unpoly.com/api/up.layer.config.md#config.mode), selects a [main target](https://unpoly.com/api/main.md) and uses [default navigation options](https://unpoly.com/api/up.fragment.config.md#config.navigateOptions):

```http
Content-Type: text/html
X-Up-Open-Layer: {}

<html>
  <main>
    Overlay content
  </main>
</html>
```

To customize the target and appearance of the new overlay, set the header value to a [relaxed JSON](https://unpoly.com/api/relaxed-json.md) string with render options:

```http
Content-Type: text/html
X-Up-Open-Layer: { target: '#menu', mode: 'drawer', animation: 'fade-in' }

<div id="menu">
  Overlay content
</div>
```

Many options from [`up.layer.open()`](https://unpoly.com/api/up.layer.open.md) are supported. [Callbacks can be passed as strings](https://unpoly.com/api/X-Up-Open-Layer.md#callbacks).

## Replacing existing overlays {#replacing-existing-overlays}

By default the new overlay is stacked on top of the current layer. There is no limit to the number of stacked layers.

To replace existing overlays instead, set `[up-layer="swap"]` or `[up-layer="shatter"]`:

| Option | Description |
| --- | --- |
| `new` | Stacks a new overlay over the current layer. |
| `swap` | Replaces the current overlay. If no overlay is open, opens an overlay. |
| `shatter` | Closes all overlays and opens a new overlay. |

<nav aria-label="Reading order">

- [Previous: Overlays](https://unpoly.com/learn/overlays.md)
- [Next: Closing overlays](https://unpoly.com/learn/closing-overlays.md)

</nav>

