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

# Closing overlays

An overlay closes when the user is done with it. The user can dismiss it with the `Escape` key or the `×` button, and the overlay's own content can close it from a button, a link, a form, JavaScript or a server response. When it closes, the overlay can pass a result value back to the layer that opened it, so the original screen can continue where the user left off.

<nav aria-label="Contents">

#### Contents

- [Accepting and dismissing](#intents)
- [Result values](#result-values)
- [Letting the opener decide](#close-conditions)
- [Closing when a button is clicked](#on-click)
- [Closing when a link is followed](#on-follow)
- [Closing when a form is submitted](#on-submit)
- [Closing from JavaScript](#from-script)
- [Closing from the server](#from-server)
- [Keeping an overlay open](#keeping-open)
- [Closing by targeting a background layer](#peeling)
- [Discarded responses](#discarded-responses)
- [Close animation](#close-animation)

</nav>

## Accepting and dismissing {#intents}

Unpoly distinguishes two intents when an overlay closes:

1. **Accepting** an overlay: the overlay closes with a positive outcome. The user confirmed with _OK_, picked a value, or created a record. An overlay can be accepted with a value, but doesn't need one.
2. **Dismissing** an overlay: the overlay closes with a negative outcome, or none at all. The user cancelled, or simply closed it with the `×` button, the `Escape` key or a click on the backdrop.

<figure>
<img src="https://unpoly.com/images/api/close-intent.png" alt="Annotated screenshot of a modal overlay and the ways to close it">
<figcaption>

A modal overlay asks the user to "Pick a character" from four buttons. Each button accepts the overlay with a different value, labeled `accept(3)` to `accept(6)`. Pressing Escape, clicking the `×` button, clicking the Cancel button or clicking the backdrop all dismiss the overlay, labeled `dismiss()`.

</figcaption>
</figure>

Acceptance usually continues a larger interaction in the parent layer, like [selecting a record that was just created](https://unpoly.com/learn/subinteractions.md). When you wait for a subtask to finish, you are interested in acceptance, but not in dismissal.

Some overlays never have a positive outcome to report. When the user only looks at an overlay, or makes changes in it that are saved right away, like adding and removing items in a list, there is nothing to confirm. Such an overlay is only ever dismissed. To update the parent layer after it closes, react to its dismissal, e.g. with [`[up-on-dismissed-reload]`](https://unpoly.com/api/up-layer-new.md#up-on-dismissed-reload).

There is no callback for an overlay that closed either way. To react to both intents, handle both.

When an overlay closes, overlays it has opened are closed with it, its pending requests are [aborted](https://unpoly.com/learn/aborting-requests.md#layers), and [focus returns](https://unpoly.com/learn/focus.md#overlays) to the link that opened it.

## Result values {#result-values}

An overlay can close with a _result value_. The value is passed to the layer that opened the overlay, which can use it to continue its own interaction: [select a new option](https://unpoly.com/learn/subinteractions.md#adding-options-to-an-existing-select), [navigate to another screen](https://unpoly.com/learn/subinteractions.md#navigating-away), or reload a list. See [Subinteractions: Working with result values](https://unpoly.com/learn/subinteractions.md#result-values) for how the opener receives the value.

### Acceptance values {#acceptance-values}

When the user selects a value, creates a record or confirms an action, the overlay is _accepted with that value_. The sections below show how a [button](#on-click), a [form](#on-submit), [JavaScript](#from-script) or the [server](#from-server) pass a value when closing.

In the opener's `[up-on-accepted]` attribute, the value is available as `value`:

```html
<a href="/select-user"
  up-layer="new"
  up-on-accepted="console.log('Got user', value)">
  Select user
</a>
```

### Dismissal values {#dismissal-values}

A dismissed overlay can pass a value too. When Unpoly's own [dismiss controls](https://unpoly.com/learn/customizing-overlays.md#customizing-dismiss-controls) close an overlay, they set the reason for dismissal as the value. For instance, clicking the `×` button dismisses with the value `":button"`:

| User interaction | Dismissal value |
| --- | --- |
| User presses the `Escape` key | `":key"` |
| User clicks the backdrop ("light dismiss") | `":outside"` |
| User clicks on the `×` button in the overlay corner | `":button"` |

An overlay that closes because a layer below it is [updated](#peeling) is dismissed with `":peel"`. You can provide your own dismissal value when closing an overlay with [`up.layer.dismiss()`](https://unpoly.com/api/up.layer.dismiss.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md).

The opener receives the value in an `{ onDismissed }` callback or `[up-on-dismissed]` attribute:

```html
<a href="/select-user"
  up-layer="new"
  up-on-dismissed="console.log('Dismissed because of', value)">
  Select user
</a>
```

## Letting the opener decide {#close-conditions}

Instead of closing the overlay from its content, the link that opens it can define a _close condition_, like reaching a URL. The overlay then closes automatically when its task is done. We recommend close conditions over the explicit commands on this page, since the overlay content then does not need to know that it is running in an overlay. See [Subinteractions: Close conditions](https://unpoly.com/learn/subinteractions.md#close-conditions).

## Closing when a button is clicked {#on-click}

To close the [current layer](https://unpoly.com/api/up.layer.current.md) when a button is clicked, set an [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) attribute:

```html
<button up-accept>Close overlay</button>
```

To close with a [result value](#result-values), set the attribute to a [relaxed JSON](https://unpoly.com/api/relaxed-json.md) value:

```html
<button up-accept="{ id: 5 }">Choose user #5</button>
```

## Closing when a link is followed {#on-follow}

When [reusing a page within an overlay](https://unpoly.com/learn/subinteractions.md#reusing-existing-screens), you may want a link that closes the overlay, but navigates somewhere else when clicked on the root layer.

For this, set [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) on a hyperlink (`<a>`) with a fallback URL in its `[href]` attribute:

```html
<a href="/list" up-accept>Finish</a>
```

Clicked in an overlay, the link accepts the overlay and the `click` event is prevented. Clicked on the [root layer](https://unpoly.com/api/up.layer.root.md), the link navigates to `/list`.

## Closing when a form is submitted {#on-submit}

To close an overlay when a form is submitted, set an [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) attribute on the `<form>` element. The overlay closes immediately on submission, without a network request:

```html
<form up-accept>
  ...
</form>
```

### Accessing the form params {#accessing-the-form-params}

The form's field values become the [result value](#result-values) of the closed overlay.

For example, this form has two fields named `foo` and `bar`:

```html
<form up-accept>
  <input name="foo" value="1">
  <input name="bar" value="2">
</form>
```

The params are exactly those a [regular submission](https://unpoly.com/api/up.Params.fromForm.md) would send, so a [submit button](https://unpoly.com/api/up.Params.fromForm.md#submit-button) with a `[name]` also contributes its value. This lets the opener see _which_ button was pressed:

```html
<form up-accept>
  <button type="submit" name="commit" value="save">Save</button>
  <button type="submit" name="commit" value="publish">Publish</button>
</form>
```

The values arrive as an [`up.Params`](https://unpoly.com/api/up.Params.md) object in the opener's `{ onAccepted }` or `{ onDismissed }` callback:

```js
up.layer.open({
  url: '/form',
  onAccepted: ({ value }) => {
    console.log(value.get('foo')) // result: "1"
    console.log(value.get('bar')) // result: "2"
  }
})
```

### Regular submission on the root layer {#regular-submission-on-the-root-layer}

When a form with [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) is submitted within an overlay, the `submit` event is prevented and no request is made.

When the same form is submitted on the root layer, there is no overlay to close. The form is then submitted regularly, e.g. with a `POST` request. This lets you [reuse an existing form within an overlay](https://unpoly.com/learn/subinteractions.md#reusing-existing-screens).

### Closing after the submission request {#closing-after-the-submission-request}

When the form should make its request _before_ the overlay closes, do _not_ set an [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) attribute. Make a regular form that is handled by Unpoly instead:

```html
<form up-submit>
  ...
</form>
```

After the server has processed the request, it can close the overlay in several ways:

- Redirecting to a [location that is a close condition](https://unpoly.com/learn/subinteractions.md#location-condition)
- Emitting an [event that is a close condition](https://unpoly.com/learn/subinteractions.md#event-condition)
- Sending a [response header that closes the overlay](#from-server)

## Closing from JavaScript {#from-script}

When you cannot use a [close condition](#close-conditions), call [`up.layer.accept()`](https://unpoly.com/api/up.layer.accept.md) to accept the [current layer](https://unpoly.com/api/up.layer.current.md):

```js
up.layer.accept()
```

To accept with a [result value](#result-values), pass it as an argument:

```js
up.layer.accept({ name: 'Anna', email: 'anna@domain.tld' })
```

To _dismiss_ an overlay from JavaScript, use [`up.layer.dismiss()`](https://unpoly.com/api/up.layer.dismiss.md) in the same fashion.

## Closing from the server {#from-server}

When you cannot use a [close condition](#close-conditions), the server can close the targeted overlay by sending an [`X-Up-Accept-Layer`](https://unpoly.com/api/X-Up-Accept-Layer.md) or [`X-Up-Dismiss-Layer`](https://unpoly.com/api/X-Up-Dismiss-Layer.md) response header. The header value is the [result value](#result-values) as [relaxed JSON](https://unpoly.com/api/relaxed-json.md), or `null` for no value:

```http
Content-Type: text/html
X-Up-Accept-Layer: { id: 123 }

<html>
  ...
</html>
```

With the `unpoly-rails` gem, you can produce these headers with [`up.layer.accept(value)`](https://unpoly.com/api/up.layer.accept.md) or [`up.layer.dismiss(value)`](https://unpoly.com/api/up.layer.dismiss.md). Libraries for other backends offer similar helpers, or you can set the headers yourself.

The server can check whether a request targets an overlay by looking at the [`X-Up-Mode`](https://unpoly.com/api/X-Up-Mode.md) request header.

When an overlay closes in reaction to a server response, you can [access the discarded response](#using-the-discarded-response).

## Keeping an overlay open {#keeping-open}

To keep an overlay open, prevent its [`up:layer:dismiss`](https://unpoly.com/api/up:layer:dismiss.md) event. Check the [dismissal value](#dismissal-values) to only ask when the user closes the overlay with the Escape key, a backdrop click or the `×` button:

```js
up.on('up:layer:dismiss', function(event) {
  let userClosed = [':key', ':outside', ':button'].includes(event.value)
  if (userClosed && !confirm('Close this overlay?')) {
    event.preventDefault()
  }
})
```

Without that check, the listener would also ask when the overlay closes because a link or form [updated a parent layer](#peeling), e.g. after a successful save. The overlay closes anyway in that case, so the question would only confuse the user.

To ask only when a form in the overlay has unsaved changes, see [Preventing loss of unsaved changes](https://unpoly.com/learn/unsaved-changes.md).

To keep only one particular overlay open, set an [`[up-on-dismiss]`](https://unpoly.com/api/up-layer-new.md#up-on-dismiss) attribute on the opening link, or pass an [`{ onDismiss }`](https://unpoly.com/api/up.layer.open.md#options.onDismiss) callback, and call `event.preventDefault()` in it. Mind the tense: `[up-on-dismiss]` runs _before_ the overlay closes, `[up-on-dismissed]` (past tense) runs _after_ it closed.

No listener can keep an overlay open when the user presses the Back button, since [going back](https://unpoly.com/learn/history-in-overlays.md#restoration) closes all overlays.

## Closing by targeting a background layer {#peeling}

When a link or form in an overlay targets a background layer, the overlay is [dismissed](#intents) once the background layer is updated. This behavior is called _peeling_.

The form below uses an [`[up-layer]`](https://unpoly.com/learn/targeting-other-layers.md) attribute to update the parent layer after a successful submission:

```html
<form method="post" action="/users" up-layer="parent">
  <input type="text" name="email">
  <button type="submit">Create user</button>
</form>
```

A successful submission dismisses the form's own overlay with a [dismissal value](#dismissal-values) of `":peel"`.

A listener cannot [keep a peeled overlay open](#keeping-open). By the time the overlay closes, the server has already processed the request.

> #### Note
>
> The form still updates its own layer when the [server responds with an error code](https://unpoly.com/learn/error-responses.md) due to a validation error.
> To update another layer in this case, set an [`[up-fail-layer]`](https://unpoly.com/api/up-follow.md#up-fail-layer) attribute.

### Accepting peeled overlays {#accepting-peeled-overlays}

By default, peeled overlays are [dismissed](#intents). To [accept](#intents) them instead, set an `[up-peel="accept"]` attribute on the link or form that targets the background layer:

```html
<form method="post" action="/users" up-layer="parent" up-peel="accept">
  ...
</form>
```

## Discarded responses {#discarded-responses}

When a server response closes an overlay, nothing from that response is rendered. This happens when the response matches a [close condition](https://unpoly.com/learn/subinteractions.md#close-conditions), or when the server [explicitly closes](#from-server) the overlay.

### Using the discarded response {#using-the-discarded-response}

Sometimes you need the discarded response anyway, e.g. to render its content in another layer.

One way is an [`[up-hungry]`](https://unpoly.com/api/up-hungry.md) element with an `[up-if-layer=subtree]` attribute in a parent layer. Such an element is updated with the discarded response of a closing overlay.

For more control, read the response from the `{ response }` property of the [`up:layer:accepted`](https://unpoly.com/api/up:layer:accepted.md) and [`up:layer:dismissed`](https://unpoly.com/api/up:layer:dismissed.md) events, and pass it to [`up.render()`](https://unpoly.com/api/up.render.md). See [Subinteractions: Reloading on acceptance](https://unpoly.com/learn/subinteractions.md#reloading-on-acceptance) for an example that updates a list without making another request.

The `{ response }` property is set whenever a server response causes an overlay to close:

- When a [server-sent event](https://unpoly.com/api/X-Up-Events.md) matches a [close condition](https://unpoly.com/learn/subinteractions.md#close-conditions).
- When the new location matches a [close condition](https://unpoly.com/learn/subinteractions.md#close-conditions).
- When the server [explicitly closes](#from-server) an overlay with an HTTP header.

### Rendering discarded notification flashes {#rendering-discarded-notification-flashes}

[Confirmation flashes](https://unpoly.com/learn/flashes.md) in a discarded response would be lost as well.

An [`[up-flashes]`](https://unpoly.com/api/up-flashes.md) element picks up flashes from a closing overlay and [renders them into the parent layer](https://unpoly.com/learn/flashes.md#from-closing-overlays).

## Close animation {#close-animation}

To close with a different animation than the one configured for the overlay, pass an `{ animation }` option to [`up.layer.accept()`](https://unpoly.com/api/up.layer.accept.md) or [`up.layer.dismiss()`](https://unpoly.com/api/up.layer.dismiss.md), or set an `[up-animation]` attribute on an [`[up-accept]`](https://unpoly.com/api/up-accept.md) or [`[up-dismiss]`](https://unpoly.com/api/up-dismiss.md) element. See [Customizing overlays: Open and close animations](https://unpoly.com/learn/customizing-overlays.md#animation) for the default animations.

<nav aria-label="Reading order">

- [Previous: Opening overlays](https://unpoly.com/learn/opening-overlays.md)
- [Next: Subinteractions](https://unpoly.com/learn/subinteractions.md)

</nav>

