Building My Own React Component Library — Part 1: The Portal
Why Build a Component Library?
Building My Own React Component Library — Part 1: The Portal
Why Build a Component Library?
Every frontend developer has, at some point, reached for UI libraries like Material-UI, Chakra, or Ant Design. They’re fantastic — until you hit customization limits, bundle size concerns, or just want full control. That’s why I decided to build Reactive, my own component library from scratch.
In this first installment, we’ll dive into one of React’s most powerful yet underappreciated features: Portals. These will become the backbone for modals, tooltips, and other “escape hatch” components in the library.
Want to follow along? Star the Reactive GitHub repo and code with me!

The Problem: Why Portals?
Imagine you’re building a modal. Normally, you’d render it inside your component tree:
function App() {
return (
<div className="app">
<Navbar />
<Modal /> {/* Renders deep inside the DOM */}
</div>
);
}
But this causes two big problems:
- Z-Index Wars: If any parent has
overflow: hiddenorz-index, your modal gets clipped or buried. - Accessibility Nightmares: Screen readers might not properly announce content nested in complex UIs.
Portals solve this by letting you render components outside the React root, while keeping them logically inside your component tree.
How Portals Work: Breaking the DOM Hierarchy
React’s createPortal lets you render a component anywhere in the DOM while preserving:
- Event bubbling (clicks inside the portal behave as if they came from the React tree)
- Context access (the portal can still consume React context)
Here’s the magic in action:
<body>
<div id="root"> <!-- Your React app -->
<App /> <!-- Modal *logic* lives here -->
</div>
<div id="portal-root"></div> <!-- Modal *DOM* renders here! -->
</body>
The modal’s logic stays in your component (for props/state), but its DOM jumps to portal-root, escaping CSS containment.
Building the Portal Component
Let’s implement this properly. Our Portal will:
- Auto-create a DOM container if missing.
- Clean up after itself when unmounted.
- Work seamlessly with SSR (no
documenterrors). - Here’s the final code (explained below):
import { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import Props from 'prop-types';
function Portal({children, containerId='portal-root'}){
const [portalElement, setPortalElement]=useState(null);
useEffect(()=>{
let portalElement=document.getElementById(containerId);
let systemGenerated=false;
if(!portalElement){
systemGenerated=true;
portalElement=document.createElement('div');
portalElement.id=containerId;
document.body.appendChild(portalElement);
}
setPortalElement(portalElement);
return ()=>{
if(systemGenerated){
document.body.removeChild(portalElement);
}
}
},[containerId]);
if(!portalElement) return null;
return ReactDOM.createPortal(children,portalElement);
}
Portal.propTypes = {
children : Props.object,
containerId: Props.string
}
export default Portal;
Using the Portal for Modals
Next time, we’ll use this Portal to build an accessible, keyboard-trapped modal. Here’s a sneak peek:
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return (
<Portal>
<div className="modal-overlay">
<div className="modal-content">
{children}
<button onClick={onClose}>Close</button>
</div>
</div>
</Portal>
);
}
This modal will:
- Auto-focus when opened.
- Trap keyboard navigation inside.
- Close on Escape key.
Let’s Build Together!
This is just the start. Reactive will grow to include:
- Modals
- Tooltips
- Notifications
- And more — all with zero dependencies!
👉 Star the GitHub repo to follow along.
Next time, we’ll enhance our Portal with TypeScript and build the modal component. Drop your questions below—I’ll answer them in the next post!
Happy coding!
Thank you for being a part of the community
Before you go:
- Be sure to clap and follow the writer ️👏️️
- Follow us: **X | [LinkedIn](https://www.linkedin.com/company/inplainenglish/) | [YouTube](https://www.youtube.com/@InPlainEnglish) | [Newsletter](https://newsletter.plainenglish.io/) | [Podcast](https://open.spotify.com/show/7qxylRWKhvZwMz2WuEoua0) | [Twitch](https://twitch.tv/inplainenglish)**
- **Start your own free AI-powered blog on Differ** 🚀
- **Join our content creators community on Discord** 🧑🏻💻
- For more content, visit **plainenglish.io + [stackademic.com](https://stackademic.com/)**
메타데이터
- post_id
- 5e76e7625130
- slug
- building-my-own-react-component-library-part-1-the-portal-5e76e7625130
- url
- https://javascript.plainenglish.io/building-my-own-react-component-library-part-1-the-portal-5e76e7625130
- canonical_url
- https://javascript.plainenglish.io/building-my-own-react-component-library-part-1-the-portal-5e76e7625130
- author_url
- https://medium.com/@akshatmtiwari
- status
- ok
- fetched_at
- 2026-06-22 17:31:34