Motion

Layer

<Layer> renders an overlay beside its screen instead of inside it, so a sheet can cover shared bars and stay correct while the screen moves.

1 min read

Wrap the overlay

Wrap the overlay in Layer inside the screen it belongs to, such as a bottom sheet that must dim the tab bar.

Inbox.tsx
import { useState } from "react";​import { Layer, Screen } from "@flemo/react";​import { ComposeSheet, MailList, TabBar } from "./ui";​export function Inbox() {  const [open, setOpen] = useState(false);​  return (    <Screen sharedBottomBar={<TabBar />}>      <MailList onCompose={() => setOpen(true)} />      <Layer>        <ComposeSheet open={open} onClose={() => setOpen(false)} />      </Layer>    </Screen>  );}

What stays with the screen

  • Only the drawing order leaves the screen. The overlay keeps its screen's stack position, status and transition, so it moves with the screen and leaves on a pop.
  • React freezes and unmounts it with the screen.
  • Children keep their positioning: position: fixed; bottom: 0 works unchanged.
  • Overlays from two screens stack in their screens' order.

When you need it

A screen at rest has no transform, so a plain position: fixed overlay with its own z-index already covers the bars. Use Layer when the screen moves under the overlay, or when the overlay must cover a header or tab bar that an ancestor screen declared.

Layer renders nothing on the server and on the first client render, before its host mounts.

Inside a nested Router

The host belongs to the outermost screen, so bottom: 0 resolves against the root Router's viewport-sized region, not the nested box. To stay inside the box, omit Layer and render ordinary content.

Why an overlay cannot do this from inside the screenDetails

A moving screen carries a transform. A transform is both the containing block for position: fixed descendants and a stacking context around everything inside it.

The shared bars live outside the screen, as its siblings. To the bars, an overlay written inside the screen is one unit with the screen's content, so "content under the bar, sheet over the bar" cannot be expressed at any z-index. Layer portals the overlay to a host beside the screens.

How the layer slot behavesDetails
  • Layer takes only children (LayerProps).
  • Each overlay renders into a slot that fills the host (position: absolute; inset: 0). The host is the root Router's region, which is fixed at full size, so bottom: 0 resolves to the same edge during a transition and at rest, and nothing jumps when a transition starts or ends.
  • The slot itself never takes pointer events, so an overlay that draws only a bottom sheet does not block taps above it. Your children get pointer events back.
  • Slots stack by the depth of the screen they belong to, not by portal mount order.
  • When a screen underneath is hidden, its overlay is hidden with it. The overlay also waits with its screen during the short pause before a transition starts.
  • When the host belongs to the overlay's own screen, the slot does not apply the transition a second time, so the overlay never moves twice as far as its screen.
Edit on GitHub