Skip to content

Backdrop component

Backdrop is the overlay primitive Modal and Drawer are both built on — a portaled, focus-trapping dim/blur layer behind arbitrary content. Reach for Modal or Drawer for their pre-built header/body/footer chrome; reach for Backdrop directly when you need the overlay behavior (stacking, focus trap, scroll lock, dismissal) around something with a fully custom layout.

Layout & Display21 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Backdrop actually gets used — each one a real, self-contained example.

Overlay primitive

Backdrop is the bare dimmed overlay behind Modal/Drawer — reach for it directly for a lightbox or a custom centered overlay. You own isOpen/onClose.

const [isOpen, setIsOpen] = useState(false);

<Button onPress={() => setIsOpen(true)}>Open overlay</Button>
<Backdrop isOpen={isOpen} onClose={() => setIsOpen(false)}>
  <div className="rounded-lg bg-paper p-6">
    <p>Click outside or press Escape to close.</p>
  </div>
</Backdrop>

Blur and opacity

blur frosts the content behind the overlay; backgroundOpacity controls how dark the scrim is.

<Backdrop
  isOpen={isOpen}
  onClose={onClose}
  blur="sm"
  backgroundOpacity={60}
>
  {content}
</Backdrop>

Entrance animation

animationType controls how the overlay and its content enter and exit.

<Backdrop
  isOpen={isOpen}
  onClose={onClose}
  animationType="zoom"
  animationDuration={0.2}
>
  {content}
</Backdrop>

Custom overlay content

Same controlled shape as Modal/Drawer — isOpen and onClose are both required, and children is whatever you want rendered above the dimmed backdrop, with total control over its layout instead of Modal's fixed header/body/footer slots.

const ImageLightbox = ({ src, isOpen, onClose }: { src: string; isOpen: boolean; onClose: () => void }) => {
  return (
    <Backdrop isOpen={isOpen} onClose={onClose} blur="lg">
      <img src={src} alt="" className="max-h-[90vh] max-w-[90vw] rounded-lg" />
    </Backdrop>
  );
};

Blur, tint, and opacity

blur accepts true (a sensible default), a size token, or a raw pixel number for the backdrop-filter blur amount. backgroundColor and backgroundOpacity control the dim layer's tint independently of the blur, for anything from a near-black scrim to a subtle, barely-there overlay.

<Backdrop isOpen={isOpen} onClose={onClose} blur="sm" backgroundOpacity={60}>
  {content}
</Backdrop>

Entrance animation

animationType picks how the backdrop and its content enter/exit (fade, zoom, or a directional slide), with animationDuration/easing to fine-tune the feel — the same animation system Modal and Drawer forward their own animation props into internally.

<Backdrop isOpen={isOpen} onClose={onClose} animationType="zoom" animationDuration={0.2}>
  {content}
</Backdrop>

Full-size content

By default the content wrapper sizes to its content and centers within the backdrop. Set contentFullSize to stretch it to fill the backdrop instead — useful for a custom overlay that needs to manage its own internal layout/scrolling edge to edge, like a full-viewport image gallery.

Props

The full public API for Backdrop. Every prop is stable and intentionally typed.

Content

children

ReactNode

default: —

Content rendered above the backdrop.

Appearance

blur

boolean | "sm" | "md" | "lg" | number

default: true (8px)

Backdrop blur amount.

backgroundColor

string

default: "var(--oks-palette-neutral-950, #000)"

Backdrop tint color.

backgroundOpacity

number

default: 80

Backdrop opacity (0-100).

Layout & positioning

zIndex

number

default: —

Explicit stacking z-index; auto-computed per overlay depth when unset.

portal

boolean

default: true

Renders through a React portal.

container

Element | DocumentFragment | null

default: —

Custom portal mount target.

contentFullSize

boolean

default: false

Stretches the content wrapper to fill the backdrop.

Behavior & states

isOpen

boolean

default: —

Controls whether the backdrop is open (required).

closeOnOutsideClick

boolean

default: true

Closes when clicking outside the content.

closeOnEscape

boolean

default: true

Closes on Escape.

lockScroll

boolean

default: true

Locks body scroll while open.

trapFocus

boolean

default: true

Traps keyboard focus within the content while open.

Motion

animationDuration

number

default: 0.3

Enter/exit animation duration in seconds.

animationType

"fade" | "zoom" | "slideUp" | "slideDown" | "slideLeft" | "slideRight"

default: fade

Entrance/exit animation.

easing

"ease" | "easeIn" | "easeOut" | "easeInOut"

default: ease

Animation easing curve.

Events

onClose

() => void

default: —

Called to close the backdrop (required).

Styling

className

string

default: —

Class applied to the backdrop layer.

style

CSSProperties

default: —

Inline styles for the backdrop layer.

contentClassName

string

default: —

Class applied to the content wrapper.

contentStyle

CSSProperties

default: —

Inline styles for the content wrapper.

Accessibility

  • Traps focus inside children while open and restores it to whatever triggered the backdrop on close, the same focus-trap behavior Modal and Drawer inherit from it.
  • lockScroll (on by default) prevents the page behind the backdrop from scrolling while it's open, keeping the user's context stable.
  • closeOnEscape and closeOnOutsideClick (both on by default) give keyboard and mouse users independent, standard ways to dismiss the overlay without you having to wire either up manually.

Best practices

  • Reach for Modal or Drawer first — they're Backdrop plus a well-tested header/body/footer layout. Drop to Backdrop directly only when your content genuinely doesn't fit that shape (a lightbox, a custom full-screen takeover).
  • Keep trapFocus on unless you have a specific reason to disable it — turning it off means keyboard focus can silently leave the overlay while it's still open, which is rarely what you want.
  • Use contentFullSize sparingly — most overlay content reads better centered and sized to itself; full-size is for genuinely edge-to-edge experiences, not a way to avoid sizing your content properly.
  • When building a custom overlay on Backdrop, keep an explicit dismiss control inside children too (not just Escape/outside-click) — a fully custom layout can otherwise leave touch or assistive-tech users without an obvious way to close it.