In this blog, we are going to explore the react portal and how can create the portal in NextJs. So first we’ll set up the NextJs App.
Understanding the react portal?
Basically, the React portal is a powerful technique to create a first-class way to render children into a DOM (Document object modal) Node from outside of their parent component hierarchy.
This means It provides the feature to render components outside of their parent component’s hierarchy. Which can be used for tooltips, alert modal boxes, and loaders.
You can see the image for the Portal example.
The following are a few steps to create a react portal in Next js.
Step 1: First We need to set up the Next Js App to implement in Next Js.
We will create the new file pages/_document. tsx in the root directory and you can see the final folder structure.
. ├── pages/ │ ├── api │ ├── hello.ts │ ├── app.tsx | ├──_documet.tsx │ └── index.tsx ├── public/ │ ├── favicon.ico │ ├── next.svg │ └── vercel.svg ├── styles/ │ └── global.css │ └── Home.module.css ├── next.config.js ├── package-lock.json ├── package.json └── README.md
Step 2: Modify the _document.tsx file to mount the react portal.
_document. tsx file should exist in the page folder it is the file equivalent to the index.html file in react Js. So i created the div element with an id mount for my portal rendered inside the div element.
//pages/_document.tsx import { poppins } from "@/components/ClientPortal"; import { Html, Head, Main, NextScript } from "next/document"; export default function Document() { return ( <Html lang="en" className={poppins.className}> <Head /> <body > <Main /> <div id="myportal" /> <NextScript /> </body> </Html> ); }
Step 3: Create a ClientPortal.tsx file inside the components directory.
Note: It created the components/ClientPortal.tsx file in the root directory. and write the code to create a portal.
//components/ClientPortal.tsx import { useEffect, useRef } from "react"; import { Poppins } from "next/font/google"; import { createPortal } from "react-dom"; type ClientPortalInterface = { children: React.ReactNode; show?: boolean; onClose?: () => void; selector: string; }; const ClientPortal = ({ children, selector, show }: ClientPortalInterface) => { const ref = useRef<Element | null>(null); useEffect(() => { ref.current = document.getElementById(selector); }, [selector]); return show && ref.current ? createPortal(children, ref.current) : null; }; export default ClientPortal; export const poppins = Poppins({ weight: ["400", "500", "600", "700"], subsets: ["latin"], });
And apply some CSS to design the content.
//styles/Home.module.css .main { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 6rem; } .removeButton{ border: none; background-color: #ffffff; cursor: pointer; padding: 5px 7px; } .removeButton:hover{ background-color: rgba(189, 195, 199, 1.0); border-radius: 6px; } /* Enable hover only on non-touch devices */ @media (hover: hover) and (pointer: fine) { .card:hover { background: rgba(var(--card-rgb), 0.1); border: 1px solid rgba(var(--card-border-rgb), 0.15); } .card:hover span { transform: translateX(4px); } } @media (prefers-reduced-motion) { .card:hover span { transform: none; } } .modal{ background: rgba(255, 255, 255,1.0); width: 60%; max-height: 500px; border-radius: 15px; padding: 20px; z-index: 100; } .header{ display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid rgba(189, 195, 199,1.0); } .header h4{ color: rgb(17 24 39 0.3); font-size: 20px; font-weight: 600; margin-bottom: 13px; } .body{ padding-top: 10px; } .body p{ color: rgba(116, 125, 140,1.0); font-size: 14px; line-height: 28px; text-align: left; } .overflow{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; background-color: rgba(0, 0, 0, 0.4); transition: 1; } .btn { padding: 8px 18px; cursor: pointer; border: 0px solid rgb(14, 158, 62); border-radius: 3px; color: rgba(255, 255, 255,1.0); font-size: 16px; border-radius: 5px; background: rgb(14, 158, 62); } .btn:hover{ background: rgb(9, 99, 39); } .groupbtn{ display: flex; justify-content: flex-end; } .cancleBtn{ padding: 10px 20px; margin: 0px 10px; cursor: pointer; border: 0px solid rgba(231, 76, 60,1.0); border-radius: 3px; color: rgba(255, 255, 255,1.0); font-size: 16px; border-radius: 5px; background: rgba(231, 76, 60,1.0); } .reactontent { margin: 20px 0px; }
Step 4: Import the ClientPortal component in the pages/index.tsx file in the root directory.
//pages/index.tsx import { useState } from "react"; import styles from "@/styles/Home.module.css"; import ClientPortal from "../components/ClientPortal"; export default function Home() { const [showPortal, setShowPortal] = useState(false); const handleModal = () => { setShowPortal(!showPortal); }; return ( <> <main className={`${styles.main}`}> <h1>Implementation portal in next JS</h1> <button onClick={handleModal} className={styles.btn}> Read Blog </button> </main> <ClientPortal selector="myportal" show={showPortal} > <div className={styles.overflow}> <div className={styles.modal}> <div className={styles.header}> <h4>Lorem Ipsum</h4> <button onClick={handleModal} className={styles.removeButton}> {/* <button className={styles.removeButton}> */} <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512" > <path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z" /> </svg> </button> </div> <div className={styles.body}> <p> Basically, React Portal is a powerful technique to create a first-class way to render children into a DOM (Document object modal) Node from outside of their parent component hierarchy. </p> <div className={styles.reactontent}> <h4>Industry's stand 1960s with the release</h4> <p> has been the industry's stand 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. </p> </div> <div className={styles.reactontent}> <p> Lorem Ipsum is simply dummy text of the printing It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently </p> </div> <div className={styles.reactontent}> <p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and Lorem Ipsum. </p> </div> </div> </div> </div> </ClientPortal> </> ); }
You can see the result on localhost.
Start your Headless Development with Webkul.
Happy Coding!!
Be the first to comment.