Reading list Switch to dark mode

    How to create a React Portal in Next Js?

    Updated 14 March 2024

    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.

    React portal Image example

    The following are a few steps to create a react portal in Next js.

    Start your headless eCommerce
    now.
    Find out More

    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.

    React Modal Box button

    Start your Headless Development with Webkul.

    Happy Coding!!

    . . .

    Leave a Comment

    Your email address will not be published. Required fields are marked*


    Be the first to comment.

    Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home