2022-11-01
2641
#react
Alexander Solovyev
11344
Nov 1, 2022 â‹… 9 min read

Learn React Portals by example

Alexander Solovyev Frontend developer and mentor @mkdev.me

Recent posts:

do not vibe code your backend

Don’t vibe code your backend: The hidden dangers of BaaS

Explore the hidden dangers of BaaS, and how frontend-focused teams can use BaaS platforms without suffering from their major risks.

Peter Aideloje
Sep 4, 2025 â‹… 6 min read
Don’t Let AI Erase The Next Generation Of Dev Leaders

Don’t let AI erase the next generation of dev leaders

As AI tools take over more routine coding work, some companies are cutting early-career dev roles — a short-sighted move that could quietly erode the next generation of tech leaders if we aren’t careful.

Jack Herrington
Sep 4, 2025 â‹… 6 min read

daisyUI 5 is here: What’s new and what to expect

Explore daisyUI 5’s new features, performance upgrades, and theming engine built for Tailwind CSS 4 developers.

Ikeh Akinyemi
Sep 4, 2025 â‹… 8 min read
React and Fluent State Article by LogRocket

Building real-time state management with React and Fluent-State

Learn a clean pattern for real-time AI chat in React using Fluent-state: immutable history, async streaming, derived UI state, and side effects without useEffect.

Ikeh Akinyemi
Sep 4, 2025 â‹… 7 min read
View all posts

8 Replies to "Learn React Portals by example"

  1. I observed a problem in this and I thought it’s important to notify, else a lot of other people might make the same mistake.

    In the `Portal` component, you are using `useEffect` with `el` and `mount` as dependencies and that actually causes unnecessary re-renders even if the prop(children in this case) of the Portal component hasn’t changed. Meaning if there is any change in the parent component which isn’t related to the Portal also, the Portal gets re-rendered.

    example:Try adding say a state variable `count` in the parent component and change the value when the modal is shown, although there is no change in portal, it would still re-render the portal which is bad.

    One Solution is to move the `mount` and `el` outside the Portal component. This still re-renders the Portal but it doesn’t mount the portal content again which would trigger useEffect otherwise as its not a new instance of either `el` or `mount`.

    Other solution would be to use the class component. But please be careful when you are demonstrating something with hooks. I think we underestimate the complexity of the hooks in most cases, and in the end we shoot in our own foot.

  2. @Brihaspati thanks for feedback. Yes, I confirm that my implementation of Portal is naive and could be improved for performance.

    Instead of just moving the el and mount outside of the component there is also an option to use some of the React hooks that keep variables between renders, for example, useRef:
    “`
    import { useEffect, useRef, memo } from “react”;
    import { createPortal } from “react-dom”;

    const Portal = ({ children }) => {
    /**
    * keeps ref between renders
    */
    const el = useRef(null);

    /**
    * create element if empty (for the first time render only)
    */
    if (!el.current) el.current = document.createElement(“div”);

    useEffect(() => {
    const mount = document.getElementById(“portal-root”);
    const { current } = el;

    mount.appendChild(current);
    return () => mount.removeChild(current);
    }, []); // no dependencies to avoid rerenders

    return createPortal(children, el.current);
    };

    export default memo(Portal);
    “`
    here is a live demo for it https://codesandbox.io/s/portal-with-hooks-70pyc

    You could also check other possible implementations of React Portal with hooks in this stackoverflow thread: https://stackoverflow.com/questions/53595935/how-can-i-make-react-portal-work-with-react-hook

  3. Hi! I was curious why we need the “el” to be appended? I tried with directly appending to the mount point in the DOM and it works as well. Does the “el” pattern help with something I am not seeing?

  4. Hi Muhammad, the el pattern is used for enabling the option of multiple portal items on the page: each portal appends its own element to the portal mount point. That might be helpful, for example, if there are multiple small popups on the page that should be kept open at the same time

  5. There’s an issue when the portal rerenders because the portal element is removed and rappended inside the useEffect hook, there’s no portal element to render to so the portal’s children content is absent. I fixed this by replacing the useEffect hook with useLayoutEffect to block react until the portal is rendered. Fortunately this is unnecessary if you instead store the portal element between rerenders like what is done above.

  6. I found a flaw following this implementation approach, which is that when the button is in the local scrolling area, clicking the button opens the tips. When scrolling, the tips will not follow. Using global scrolling does not have this problem because the tips do not scroll relative to the body

Leave a Reply