Radix Dialog stacking styling #3667
Replies: 2 comments
-
|
This is a common challenge with stacked Radix Dialogs. The issue is that each Dialog renders its own Here are a few approaches: 1. Hide the first overlay when a second dialog opens Track which dialogs are open and conditionally style the overlay: const [firstOpen, setFirstOpen] = useState(false);
const [secondOpen, setSecondOpen] = useState(false);
<Dialog.Root open={firstOpen} onOpenChange={setFirstOpen}>
<Dialog.Portal>
<Dialog.Overlay
style={{ opacity: secondOpen ? 0 : 1, transition: "opacity 150ms" }}
/>
<Dialog.Content>
{/* Button that opens second dialog */}
<button onClick={() => setSecondOpen(true)}>Open nested</button>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
<Dialog.Root open={secondOpen} onOpenChange={setSecondOpen}>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>...</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>2. Use a single shared overlay with z-index stacking Render one overlay outside both dialogs, and only render the .shared-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 49;
}
.dialog-content-first {
z-index: 50;
}
.dialog-content-second {
z-index: 51;
}Then conditionally show the shared overlay when any dialog is open. 3. CSS stacking context trick If you want to keep both overlays but only show the top one visually: /* All overlays share the same z-index */
[data-radix-dialog-overlay] {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 50;
}
/* Only the last overlay is visible — earlier ones are behind content */
[data-radix-dialog-overlay] ~ [data-radix-dialog-overlay] {
background: rgba(0, 0, 0, 0.3); /* lighter or transparent */
}Base UI handles this internally by managing a stack of open dialogs and only rendering one backdrop for the topmost. Radix takes the "unstyled primitives" approach and leaves this to you. Approach #1 is the most straightforward if you only have a known set of dialogs. |
Beta Was this translation helpful? Give feedback.
-
|
I hit this same issue. Stacking dialogs where each one renders its own backdrop, and they pile up visually. overlay-kit doesn't remove the duplicate backdrops by itself, but it gives each dialog an independent lifecycle, so you can control which one renders a backdrop and which doesn't: // First dialog: renders backdrop
overlay.open(({ isOpen, close }) => (
<Dialog open={isOpen} onOpenChange={close}>
<DialogOverlay />
<DialogContent>
<button onClick={() => {
// Second dialog: skip backdrop, or style it differently
overlay.open(({ close: closeInner }) => (
<Dialog open onOpenChange={closeInner}>
<DialogContent>Nested!</DialogContent>
</Dialog>
))
}}>Open nested</button>
</DialogContent>
</Dialog>
))Since each overlay's mount/unmount is independent, closing the inner one doesn't affect the outer. Made stacking much easier to manage for me. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hey,

I currently have a Radix Dialog based modal. My current setup is having the modal portaled to the body and with a fixed z-index for the overlay and the modal.
However that becomes a problem when I have two. The second one stacks on top of the first one, but that leaves two overlays below (check the screenshot).
I found an example in Base UI, which seems to be very closely related to how Radix works, that has nested dialogs, but I don't see anything specific in the structure or CSS to indicate support, so I assume it is somehow internally handled (https://base-ui.com/react/components/dialog#nested-dialogs)?
Has anyone done that kind of support with the Radix Dialog?
Beta Was this translation helpful? Give feedback.
All reactions