This article explains how to display a modal dialog using Reactjs portals. A fully functional sample react project is available to download at the end of this article.
Modals are reusable message containers that are displayed on top of the screen, blocking background contents. Modals usually put an overlay on the screen to block interaction with the parent screen. Therefore, it is good practice to keep the modal separate from the rest of the application code. React portals allow us to render content into a different part of the dom, enabling us to maintain the modal in a separate dom node.
When you create the react project, the package manager automatically creates a root element to mount the main application. This HTML element is located in the index.html. Now we have to add another element to mount our modal.
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal-root"></div>
</body>
</html>
The next step is to mount the modal in the newly created dom element. The modal code is listed below. Most of the code follows a regular React component pattern, except for how we render the component to the dom. The RenderModal component accepts a ReactNode as props and mounts it in the modal-root dom element.
import { ReactNode } from "react"
import ReactDOM from 'react-dom';
export interface CardProps {
children: ReactNode
}
export const RenderModal = (props:CardProps) => {
return ReactDOM.createPortal(props.children, document.getElementById("modal-root")!)
}
Listed below is a simple modal component to display a popup with an overlay.
import styles from "./local.module.css"
interface ModalProps {
onClose: (showModal: boolean) => void
}
export const YesNoDialogModal = (props: ModalProps) =>{
return(
<div className={styles.modal}>
<span onClick={() => props.onClose(false)} className={styles.close}>×</span>
<form className={styles.modalContent}>
<div className={styles.container}>
<h1>We use cookies</h1>
<p>Would you like to proceed?</p>
<div className={styles.clearfix}>
<button type="button" onClick={() => props.onClose(false)} className={styles.cancelbtn}>Cancel</button>
<button type="button" onClick={() => props.onClose(false)} className={styles.deletebtn}>Ok</button>
</div>
</div>
</form>
</div>
)
}
Simple logic to display and close the modal.
import React, { useState } from 'react';
import { RenderModal } from './components/modal/RenderModal';
import { YesNoDialogModal } from './components/modal/YesNoDialogModal';
export const App = () => {
const [showModal, setShowModal] = useState(false)
return (
<>
<RenderModal>
{showModal && <YesNoDialogModal onClose={setShowModal}/>}
</RenderModal>
<h2>Cookie Modal</h2>
<button onClick={() => setShowModal(true)}>Open Modal</button>
</>
);
}
export default App;
If you inspect the dom using chrome developer tools, you will notice the additional dom element we added to the index.tsx. The open modal button adds the modal HTML to the modal-root dom element. The HTML clears when the modal is closed.
Download Reactjs modal and portal example