# Using the lightbox inside a modal/dialog #414
igordanchenko
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Using the lightbox inside a modal/dialog
Occasionally, you may want to open the lightbox from inside a modal/dialog (for example, an image grid inside a modal view). Because both the lightbox and most dialog libraries portal to
<body>and install global side effects (focus traps, body scroll locking, document-level outside-click listeners), they can interfere with each other in subtle ways.This document summarizes what works out of the box, what requires a small amount of configuration, and the reasoning behind the recommendations.
Compatibility
"Status" here covers four interactions: clicking the lightbox prev/next buttons, dragging to swipe between slides, pressing Escape to close the lightbox (but not the parent dialog), and clean teardown — body styles restored,
aria-hiddencleared, and no leftoverinertattributes.What the lightbox does on its own
The lightbox now ships with several behaviors that make nested usage work without configuration in the majority of cases:
pointer-events: autoon.yarl__portal. Some libraries (Radix, Chakra) setbody { pointer-events: none }while their dialog is open as an outside-click signal. Because the lightbox portals into<body>, it would inherit that and become non-interactive. This CSS override shields the lightbox from that cascade.event.stopPropagation()on handledpointerdown. When the user interacts with the lightbox controller, the lightbox consumes the event so bubble-phase outside-click listeners ondocument(Radix) do not also dismiss the parent dialog.event.stopPropagation()on handledkeydown(Escape, ←, →). Same rationale: a key the lightbox handles should not also be handled by the parent dialog.Libraries with capture-phase listeners
Some dialog libraries register their Escape handlers (and, in Chakra's case, outside-click handlers) in the capture phase on
document. The capture phase runs before React's delegation root has a chance to dispatch the lightbox'sonKeyDown/onPointerDown, so the lightbox'sstopPropagation()cannot prevent them. The fix has to come from the parent dialog: tell it not to dismiss while the lightbox is open.Radix
This also applies to shadcn/ui
<Dialog>, which is a thin wrapper around Radix.Outside-click works without a workaround — Radix uses the bubble phase for
pointerdown, so the lightbox's built-instopPropagation()already disarms it.Mantine
Outside-click works without a workaround for the same reason.
Chakra UI
Chakra v3 wraps Ark UI / Zag (which other libraries also build on) and uses the capture phase for both outside-click and Escape:
Body scroll lock
Most dialog libraries lock
<body>scrolling while open (overflow: hidden+padding-rightcompensation for the disappearing scrollbar). The lightbox does the same by default. When nested, both apply locks to the same element, which can cause snapshot/restore drift: tiny scrollbar flashes, leftoverpadding-right, and similar artifacts.Recommendation: when the parent dialog already locks body scroll (as most libraries do), set
noScroll={{ disabled: true }}on the lightbox so it does not apply a redundant second lock:Exception: react-modal does not lock body scrolling by default — it only adds the
ReactModal__Body--openclass to<body>and leaves the actualoverflow: hiddenrule up to the consumer's stylesheet. Either:noScrollenabled (the default), or.ReactModal__Body--open { overflow: hidden }globally so the modal manages scrolling for its entire subtree (and then disable the lightbox'snoScrollas shown above).Beta Was this translation helpful? Give feedback.
All reactions