Learn 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.

To open a link's destination in a modal overlay, set an [up-layer=new] attribute:

<a href="/menu" up-layer="new">Open menu</a> <!-- mark: up-layer="new" -->

When the user clicks, Unpoly requests /menu and shows the response's main fragment 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

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 for overlays. Set [up-main=overlay] for all overlays, or name a mode like [up-main=modal]:

    <body>
      <div class="layout" up-main="root"> <!-- mark: up-main="root" -->
        <nav class="layout--side">...</nav>
        <div class="layout--content" up-main="overlay"> <!-- mark: 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] attribute:

    <a href="/menu" up-layer="new" up-target=".menu-items">Open menu</a> <!-- mark: up-target=".menu-items" -->
    
  3. Render something different on the server. Requests for an overlay carry an X-Up-Mode header with the overlay's mode.

When the server responds with an error code, 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].

When the user clicks a second overlay link while the first is still loading, only the second overlay opens. See aborting rules in layers.

Choosing the overlay mode

By default the overlay opens as a modal. To use a different mode, like a popup or drawer, set an [up-mode] attribute:

<a href="/menu" up-layer="new" up-mode="drawer">Open menu</a> <!-- mark: up-mode="drawer" -->

As a shorthand, you can append the mode to the [up-layer=new] attribute:

<a href="/menu" up-layer="new drawer">Open menu</a> <!-- mark: up-layer="new drawer" -->

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.

Opening an overlay from a form

To show the response to a successful form submission in an overlay, set an [up-layer=new] attribute on the form:

<form method="post" action="/subscribers" up-layer="new"> <!-- mark: up-layer="new" -->
  ...
</form>

To only use an overlay for error messages from a failed submission, set an [up-fail-layer=new] attribute instead:

<form method="post" action="/subscribers" up-fail-layer="new"> <!-- mark: up-fail-layer="new" -->
  ...
</form>

Opening overlays from local content

The [up-content] attribute opens an overlay without going through the server:

<a up-layer="new popup" up-content="<p>Helpful instructions here</p>"> <!-- mark: 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.

Instead of embedding an HTML string, you can also refer to a template:

<a up-layer="new popup" up-content="#help"> <!-- mark: up-content="#help" -->
  Help
</a>

<template id="help"> <!-- mark: id="help" -->
  <p>Helpful instructions here</p>
</template>

Opening overlays from JavaScript

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

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

Instead of up.navigate() or up.render() you can also call up.layer.open(). It returns a promise for the new up.Layer instance:

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

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

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

To get a promise for the overlay's result value instead, use up.layer.ask():

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.

Opening overlays from the 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, selects a main target and uses default navigation options:

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 string with render options:

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() are supported. Callbacks can be passed as strings.

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.