Could not apply CSS on React portal content

1.4k Views Asked by At

I'm trying to use React portal to render a portal, so I created two div. One for backdrop and one for actual portal inside index.html file.

 <div id="backdrop-root"></div>
 <div id="overlay-root"></div>
 <div id="root"></div>

My Portal code:

const Backdrop = () => {
  return <div className="backdrop" />;
};
const ModalOverlay = () => {

  return (
    <div css={NewTransactionPortalStyles} className="modal">
      <Card>
        <div className="modal-header">
          <div>Create Transaction</div>
          <div>
            <XLg size={23} />
          </div>
        </div>
        <div className="grid-styles">
          <div> This is my Portal </div>
        </div>
      </Card>
    </div>
  );
};

export const NewTransactionPortal = () => {
  return (
    <div css={NewTransactionPortalStyles}>
      {ReactDOM.createPortal(
        <Backdrop />,
        document.getElementById("backdrop-root")!
      )}
      {ReactDOM.createPortal(
        <ModalOverlay transaction={[]} />,
        document.getElementById("overlay-root")!
      )}
    </div>
  );

CSS content:

export const NewTransactionPortalStyles = css`
    .backdrop {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100vh;
      z-index: 10;
      background: rgba(0, 0, 0, 0.75);
    }

    .modal {
      position: fixed;
      top: 30vh;
      left: 10%;
      width: 60%;
      height: 50%
      z-index: 100;
      overflow: hidden;
    }

I don't know what is missing but my CSS class is not applying on my portal, I can see I'm able to target the div but not able to apply CSS on it, so as a result I'm not able to get the required result. I'm using emotion library for my CSS here. Dev tool image

2

There are 2 best solutions below

0
ghybs On

When using React Portal, the portal content is actually in a different DOM position than what one may infer from the React components tree: the portal content is placed as the specified element descendant.

This can be seen in your screenshot:

  • the backdrop div is a child of backdrop-root
  • the modal is a child of overlay-root
  • both are not descendants of root

(That is the expected result of using React Portal)

As such, if you apply your CSS rules on an element within root, it will have no style effect on your portal contents, since they are not actual DOM descendants of that element (even though you created them in React from a child component).

In your case, you could just apply your style directly on the backdrop and modal.

It seems you did try for the modal, but because the style is applied on the same element that has the .modal className, it does not match your CSS which targets a .modal within the element that receives the styling.

You could just do for example:

export const ModalStyles = css`
      position: fixed;
      top: 30vh;
      left: 10%;
      width: 60%;
      height: 50%
      z-index: 100;
      overflow: hidden;
`;

const ModalOverlay = () => {
  return (
    <div css={ModalStyles}>
      {/* etc. */}
    </div>
  );
};
0
KingCodeFish On

I ran into a similar problem with my project. Unfortunately, most of my CSS was global and included into the JS file instead of assigned at the component level. What I ended up doing was just setting the innerHTML of the head tag on the window to the one I was using for the main window:

const NewWindow = ({ children }) => {
  const newWindow = React.useMemo(() => window.open('', 'New Window'), [children]);
  newWindow.document.head.innerHTML = window.document.head.innerHTML;
  return ReactDOM.createPortal(children, newWindow.document.body);
};

May or may not be applicable to your use case though.