DEV Community

Phaneendra Kanduri
Phaneendra Kanduri

Posted on

Stacking Multiple Dialogs in React Without Hooks or Effects

Managing z-index across multiple stacked dialogs in React gets messy fast. I ran this problem through an AI tool out of curiosity and it generated this:

jsxconst DialogManager = ({ dialogs }) => {
  return (
    <>
      {dialogs.map((dialog, index) => {
        const zIndexBackdrop = 1000 + (5 * (index + 1));
        const zIndexContent = zIndexBackdrop + 2;

        return (
          <div key={dialog.id}>
            <div
              className="modal-backdrop show"
              style={{ zIndex: zIndexBackdrop }}
            />
            <div
              role="dialog"
              aria-modal="true"
              style={{ zIndex: zIndexContent }}
            >
              {dialog.content}
            </div>
          </div>
        );
      })}
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Functional, but it runs the z-index calculation on every render cycle. For a static stacking requirement, that's unnecessary execution. Styling problems should be solved in the style layer.

Here's the approach I came up with using SCSS:

@for $i from 1 through 6 {
  $zIndexBackdrop: #{1000 + (5 * $i)};
  $zIndexContent:  #{1000 + (5 * $i) + 2};

  .modal-backdrop.show:nth-of-type(#{$i}) {
    z-index: $zIndexBackdrop;
  }

  div[role="dialog"][aria-modal="true"]:nth-of-type(#{$i}) {
    z-index: $zIndexContent;
  }
}
Enter fullscreen mode Exit fullscreen mode

This loop generates z-index rules for up to 6 dialog layers at compile time - zero runtime cost. The backdrop and content for each layer are spaced 2 units apart, with 5-unit gaps between layers to leave room for other elements if needed.

A few things to know before using this:

  • nth-of-type is sensitive to DOM structure. If other elements of the same type exist in the same context, the selector can misfire. Verify your rendered DOM matches the assumption.
  • The ceiling of 6 is arbitrary — adjust the loop range, base value, and step size to fit your z-index scale.
  • This compiles down to plain CSS. No runtime, no state, no effect hooks. That's the point.

Top comments (0)