React Portal with dynamic mounting support
npm install react-magic-portalA React component designed for browser extension development that provides react portal functionality with automatic anchor detection and DOM mutation monitoring.
    
``bash`
$ pnpm add react-magic-portal
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.
- Dynamic Anchor Detection - Automatically detects when target elements appear or disappear in the DOM
- Multiple Positioning Options - Support for last, first, before, and after positioningonMount
- Flexible Anchor Selection - Support for CSS selectors, element references, functions, and direct elements
- Lifecycle Callbacks - and onUnmount callbacks for portal lifecycle management
- TypeScript Support - Full TypeScript support with comprehensive type definitions
`jsx
import MagicPortal from 'react-magic-portal'
function App() {
const [showTarget, setShowTarget] = useState(false)
return (
{showTarget &&
{/ Portal will automatically mount/unmount based on target availability /}
onMount={() => console.log('Portal mounted')}
onUnmount={() => console.log('Portal unmounted')}
>
$3
`jsx
import MagicPortal from 'react-magic-portal'function App() {
return (
Target Element
Content before target
Content at start of target
Content at end of target
Content after target
)
}
`API Reference
$3
| Prop | Type | Default | Description |
| ----------- | ------------------------------------------------------------------------------------------ | --------------- | ------------------------------------------------------------ |
|
anchor | string \| (() => Element \| null) \| Element \| React.RefObject | 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 |$3
#### CSS Selector String
`jsx
Content
Content
`#### Element Reference
`jsx
const elementRef = useRef(null)Target
Content
`#### Function
`jsx
document.querySelector('.anchor')}>
Content
`#### Direct Element
`jsx
Content
`$3
####
last (default)Adds content inside the anchor element at the end:
`html
Existing content
`####
firstAdds content inside the anchor element at the beginning:
`html
Existing content
`####
beforeAdds content as a sibling before the anchor element:
`html
Existing content
`####
afterAdds content as a sibling after the anchor element:
`html
Existing content
`Important Notes
$3
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+)
`jsx
interface MyComponentProps {
ref?: React.Ref
}const MyComponent = ({ ref, ...props }: MyComponentProps) => {
return
My Component Content
}// This will work correctly
`#### ✅ Works - Components with forwardRef (React 18 and earlier)
`jsx
import { forwardRef } from 'react'const MyComponent = forwardRef((props, ref) => {
return My Component Content
})
// This will work correctly
`#### ✅ Works - Direct DOM elements
`jsx
// Direct DOM elements always work
Direct DOM element
`#### ❌ Won't work - Components without ref support
`jsx
const MyComponent = () => {
return My Component Content
}// This won't position correctly because ref cannot be passed to the component
`#### ✅ Solution - Wrap with DOM element
`jsx
const MyComponent = () => {
return My Component Content
}// Wrap the component in a transparent DOM element
``MIT © molvqingtai