Creating a portal in Next Js differs from creating a portal in React Js. React Js generally run on the client side, while Next Js run on the server side. My modal renders on the client side. Therefore, in Next Js, I must ensure that the DOM is ready and the container is available to mount the portal.
Here are the steps to create a portal in Next Js.
I use Typescript. You should too if possible. So my file extension is tsx.
My
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
I created a div
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<div id="portal" />
<NextScript />
</body>
</Html>
)
}
Not it is the time to create a component to create a portal and mount it to the DOM node.
import { useRef, useEffect, useState, ReactNode } from 'react'
import { createPortal } from 'react-dom'
import styles from "./Overlay.module.css"
interface PortalProps {
children: ReactNode
}
export const Portal = (props: OverlayProps) => {
const ref = useRef<Element | null>(null)
const [mounted, setMounted] = useState(false)
useEffect(() => {
ref.current = document.querySelector<HTMLElement>("#portal")
setMounted(true)
}, [])
return (mounted && ref.current) ? createPortal(<div className={styles.overlay}>{props.children}</div>, ref.current) : null
}
Here are the styles to go with the portal
.overlay {
display: block;
position: fixed;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
z-index: 2;
}
I am doing a few things differently here. I use a ref to hold the DOM node. That ensures I get the same reference to the Node in every re-render.
I use the state variable mounted to ensure my component is mounted and
The usage of this component is easy.