A small React portal library made with hooks. Allows you to render an indefinite number of portals without having to define them in advance. Useful for event-driven notifications or modals where you don't know how many items will be rendered at a given time.
To use react-portal-hook in your project, run:
npm install react-portal-hook
Wrap your root component with PortalProvider
.
// app.jsx
import { PortalProvider } from "react-portal-hook";
const App = () => {
return (
<PortalProvider>
<RootComponent />
</PortalProvider>
);
};
Example - Modals:
By default, portals will be appended to document.body
.
// page.jsx
import { usePortals } from "react-portal-hook";
const Modal = ({ closeModal }) => {
return (
<div>
<h2>Title</h2>
<p>This is a modal</p>
<button onClick={closeModal}>Close Modal</button>
</div>
);
};
export const Page = () => {
const portalManager = usePortals();
const openModal = () => {
portalManager.open(portal => <Modal closeModal={portal.close} />);
};
return (
<div>
<h2>Title</h2>
<p>This is a page</p>
<button onClick={openModal}>Open Modal</button>
</div>
);
};
Example - Notifications:
You can specify a DOM node in which to render portals with the appendTo
option:
// layout.jsx
import { useRef } from "react";
import { usePortals } from "react-portal-hook";
const Notification = ({ closeNotification }) => {
return (
<div>
<p>
This is a notification{" "}
<button onClick={closeNotification}>Close Notification</button>
</p>
</div>
);
};
export const Layout = () => {
const portalManager = usePortals();
const notificationEl = useRef();
const showNotification = () => {
// Calling this from anywhere in your app will render a notification
portalManager.open(
portal => <Notification closeNotification={portal.close} />,
{
appendTo: notificationEl.current
}
);
};
return (
<div>
<div id="notification-holder" ref={notificationEl} />
<button onClick={showNotification}>Show Notification</button>
</div>
);
};
interface PortalManager {
/**
* The react element you want to render in the portal
*/
element: ((portal: Portal) => React.ReactElement) | React.ReactElement;
options?: {
/**
* An ID to avoid duplicate portals
*/
id?: string;
/**
* A DOM node in which to render the portal
*/
appendTo?: Element;
/**
* A callback that is fired when the portal closes
*/
onClose?: () => void;
};
}
interface Portal {
/**
* A function to close the Portal
*/
close: () => void;
}