npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-magic-portal

v1.4.0

Published

React Portal with dynamic mounting support

Readme

React Magic Portal

A React component designed for browser extension development that provides react portal functionality with automatic anchor detection and DOM mutation monitoring.

version workflow download npm package minimized gzipped size Ask DeepWiki

$ pnpm add react-magic-portal

View Demo

Introduction

When developing browser extensions with React, you often need to inject React components into host web pages. However, the target mounting points in these pages are frequently dynamic - they may not exist when your extension loads, or they might be created and destroyed as users navigate and interact with the page.

Traditional React portals require the target DOM element to exist before rendering, which creates challenges in browser extension scenarios where:

  • Host pages load content dynamically - Target elements appear after AJAX requests, user interactions, or page navigation
  • Single Page Applications (SPAs) - Content changes without full page reloads, causing mounting points to appear and disappear
  • Dynamic DOM manipulation - Third-party scripts or frameworks modify the DOM structure continuously
  • Uncertain timing - You can't predict when or if target elements will be available

React Magic Portal solves these challenges by automatically detecting when target elements appear or disappear in the DOM, ensuring your React components are always rendered in the right place at the right time.

Features

  • Dynamic Anchor Detection - Automatically detects when target elements appear or disappear in the DOM
  • Multiple Positioning Options - Support for last, first, before, and after positioning
  • Flexible Anchor Selection - Support for CSS selectors, element references, functions, and direct elements
  • Lifecycle Callbacks - onMount and onUnmount callbacks for portal lifecycle management
  • TypeScript Support - Full TypeScript support with comprehensive type definitions

Usage

Dynamic Content Loading

import MagicPortal from 'react-magic-portal'

function App() {
  const [showTarget, setShowTarget] = useState(false)

  return (
    <div>
      <button onClick={() => setShowTarget(!showTarget)}>Toggle Target</button>

      {showTarget && <div id="anchor-target">Dynamic Target Element</div>}

      {/* Portal will automatically mount/unmount based on target availability */}
      <MagicPortal
        anchor="#anchor-target"
        onMount={() => console.log('Portal mounted')}
        onUnmount={() => console.log('Portal unmounted')}
      >
        <div>This content follows the target element</div>
      </MagicPortal>
    </div>
  )
}

Multiple Portals on Same Anchor

import MagicPortal from 'react-magic-portal'

function App() {
  return (
    <div>
      <div id="target">Target Element</div>

      <MagicPortal anchor="#target" position="before">
        <div>Content before target</div>
      </MagicPortal>

      <MagicPortal anchor="#target" position="first">
        <div>Content at start of target</div>
      </MagicPortal>

      <MagicPortal anchor="#target" position="last">
        <div>Content at end of target</div>
      </MagicPortal>

      <MagicPortal anchor="#target" position="after">
        <div>Content after target</div>
      </MagicPortal>
    </div>
  )
}

API Reference

Props

| Prop | Type | Default | Description | | ----------- | ------------------------------------------------------------------------------------------ | --------------- | ------------------------------------------------------------ | | anchor | string \| (() => Element \| null) \| Element \| React.RefObject<Element \| null> \| null | Required | The target element where the portal content will be rendered | | position | 'last' \| 'first' \| 'before' \| 'after' | 'last' | Position relative to the anchor element | | root | Element | document.body | The root element to observe for DOM mutations | | children | React.ReactElement \| null | undefined | A single React element to render in the portal (does not support Fragment) | | onMount | (anchor: Element, container: Element) => void | undefined | Callback fired when the portal is mounted | | onUnmount | (anchor: Element, container: Element) => void | undefined | Callback fired when the portal is unmounted |

Anchor Types

CSS Selector String

<MagicPortal anchor="#anchor">
  <div>Content</div>
</MagicPortal>

<MagicPortal anchor=".anchor">
  <div>Content</div>
</MagicPortal>

Element Reference

const elementRef = useRef(null)

<div ref={elementRef}>Target</div>

<MagicPortal anchor={elementRef}>
  <div>Content</div>
</MagicPortal>

Function

<MagicPortal anchor={() => document.querySelector('.anchor')}>
  <div>Content</div>
</MagicPortal>

Direct Element

<MagicPortal anchor={document.body}>
  <div>Content</div>
</MagicPortal>

Position Options

last (default)

Adds content inside the anchor element at the end:

<div id="anchor">
  Existing content
  <!-- Portal content appears here -->
</div>

first

Adds content inside the anchor element at the beginning:

<div id="anchor">
  <!-- Portal content appears here -->
  Existing content
</div>

before

Adds content as a sibling before the anchor element:

<!-- Portal content appears here -->
<div id="anchor">Existing content</div>

after

Adds content as a sibling after the anchor element:

<div id="anchor">Existing content</div>
<!-- Portal content appears here -->

Important Notes

React Component Ref Requirements

When using React components as children, they must support ref forwarding to work correctly with MagicPortal. This is because MagicPortal needs to access the underlying DOM element to position it correctly.

✅ Works - Components with ref props (React 19+)

interface MyComponentProps {
  ref?: React.Ref<HTMLDivElement>
}

const MyComponent = ({ ref, ...props }: MyComponentProps) => {
  return <div ref={ref}>My Component Content</div>
}

// This will work correctly
<MagicPortal anchor="#target">
  <MyComponent />
</MagicPortal>

✅ Works - Components with forwardRef (React 18 and earlier)

import { forwardRef } from 'react'

const MyComponent = forwardRef<HTMLDivElement>((props, ref) => {
  return <div ref={ref}>My Component Content</div>
})

// This will work correctly
<MagicPortal anchor="#target">
  <MyComponent />
</MagicPortal>

✅ Works - Direct DOM elements

// Direct DOM elements always work
<MagicPortal anchor="#target">
  <div>Direct DOM element</div>
</MagicPortal>

❌ Won't work - Components without ref support

const MyComponent = () => {
  return <div>My Component Content</div>
}

// This won't position correctly because ref cannot be passed to the component
<MagicPortal anchor="#target">
  <MyComponent />
</MagicPortal>

✅ Solution - Wrap with DOM element

const MyComponent = () => {
  return <div>My Component Content</div>
}

// Wrap the component in a transparent DOM element
<MagicPortal anchor="#target">
  <div style={{ display: 'contents' }}>
    <MyComponent />
  </div>
</MagicPortal>

License

MIT © molvqingtai