UnflowUI
Components

Backdrop

A full-screen overlay that traps focus, locks scroll, and closes on click-away or Esc — the primitive behind Alert and other modal surfaces.

Overview

Backdrop renders a dark overlay that covers the viewport (or a relative parent) and positions child content on top of it. It handles:

  • Scroll lockdocument.body.overflow is set to hidden while open, and padding is added to compensate for the removed scrollbar.
  • Focus trap — keyboard focus is constrained to the children while the backdrop is open.
  • Click-away and EsconClose is called when the user clicks outside the children or presses Esc.
  • Mount/unmount lifecycle — children are mounted on open and unmounted after the closing transition completes.

Backdrop is the building block used by Alert. Use it directly when you need a custom modal surface.

Import

import { Backdrop } from '@unflow.io/ui/components/Backdrop';

Props

PropTypeDefaultDescription
openbooleanControls visibility
onClose() => voidCalled on click-away or Esc
childrenReactElementRequired. Content rendered over the overlay
disablePortalbooleanUse absolute positioning relative to parent instead of fixed viewport overlay
keepMountedbooleanKeep children mounted (invisible) in the DOM while closed, instead of unmounting them
autoFocusbooleanMove focus into children when the backdrop opens

Basic usage

'use client';
import { useState } from 'react';
import { Backdrop } from '@unflow.io/ui/components/Backdrop';
import { Button } from '@unflow.io/ui/components/Button';

export function Example() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button label="Open backdrop" onClick={() => setOpen(true)} />
      <Backdrop open={open} onClose={() => setOpen(false)}>
        <div className="bg-white rounded-lg shadow-lg p-6">
          <p className="text-sm font-medium">Custom content over backdrop</p>
          <Button label="Close" variant="secondary" size="sm" onClick={() => setOpen(false)} />
        </div>
      </Backdrop>
    </>
  );
}

Relative to parent

Set disablePortal to render the backdrop inside its DOM parent with absolute positioning instead of as a fixed viewport overlay. The parent element must have position: relative and a defined size.

<div className="relative h-64 w-full border rounded-lg overflow-hidden">
  <Button label="Open" onClick={() => setOpen(true)} />
  <Backdrop open={open} disablePortal onClose={() => setOpen(false)}>
    <div className="bg-white rounded-lg shadow-lg p-6">
      <p className="text-sm">Contained to parent</p>
    </div>
  </Backdrop>
</div>

Custom backdrop overlay

Replace the default bg-black/30 dark overlay using slotProps.backdrop. Any Tailwind class or inline style can be applied.

<Backdrop
  open={open}
  onClose={() => setOpen(false)}
  slotProps={{ backdrop: { className: 'bg-brand-500/40' } }}
>
  <div className="bg-white rounded-lg p-6">Content</div>
</Backdrop>

To replace the overlay element entirely (e.g. with a blurred surface), use slots.backdrop:

<Backdrop
  open={open}
  onClose={() => setOpen(false)}
  slots={{ backdrop: <span className="absolute inset-0 backdrop-blur-sm bg-white/10" /> }}
>
  <div className="bg-white rounded-lg p-6">Content</div>
</Backdrop>

Customization (slotProps)

SlotPropsDescription
backdropBaseHTMLProps<HTMLSpanElement>The dark overlay <span> behind the children

Accessibility

  • The container itself carries no ARIA role — it's a generic overlay wrapper. Give the content you pass as children its own role (e.g. dialog/alertdialog) and accessible name, as Alert and Modal do.
  • Focus is trapped within children while open, preventing keyboard users from tabbing to background content.
  • Pressing Esc triggers onClose — always wire this to actually close the overlay.
  • Set autoFocus to move focus into the children when the backdrop opens, so keyboard and screen-reader users land in the right place.
  • Scroll lock prevents background content from scrolling while the overlay is open. The removed scrollbar width is compensated with paddingRight to avoid layout shift.