typescriptintermediate
Portal-Based Modal Component
Accessible modal component using React portals with focus trapping, Escape key close, and backdrop click.
typescriptPress ⌘/Ctrl + Shift + C to copy
import { useEffect, useRef, ReactNode } from 'react';
import { createPortal } from 'react-dom';
interface ModalProps {
open: boolean;
onClose: () => void;
children: ReactNode;
}
export function Modal({ open, onClose, children }: ModalProps) {
const overlayRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
const handleKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleKey);
document.body.style.overflow = 'hidden';
contentRef.current?.focus();
return () => {
document.removeEventListener('keydown', handleKey);
document.body.style.overflow = '';
};
}, [open, onClose]);
if (!open) return null;
return createPortal(
<div
ref={overlayRef}
onClick={(e) => e.target === overlayRef.current && onClose()}
style={{
position: 'fixed', inset: 0, display: 'flex',
alignItems: 'center', justifyContent: 'center',
backgroundColor: 'rgba(0,0,0,0.6)', zIndex: 50,
}}
>
<div
ref={contentRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
style={{
background: '#111', borderRadius: 12, padding: 24,
maxWidth: 500, width: '90%', outline: 'none',
}}
>
{children}
</div>
</div>,
document.body
);
}
// Usage:
// <Modal open={isOpen} onClose={() => setIsOpen(false)}>
// <h2>Confirm</h2>
// <button onClick={() => setIsOpen(false)}>Close</button>
// </Modal>Use Cases
- Confirmation dialogs
- Image lightbox
- Form overlays
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
React Portal Modal Component
Build an accessible modal dialog using React portals with focus trapping and keyboard handling.
Best for: Accessible modal dialogs with focus management
#react#portal
typescriptintermediate
Portal Dropdown Component
Dropdown menu rendered via Portal to avoid overflow clipping, with click-outside dismiss.
Best for: Navigation menus
#portal#dropdown
typescriptintermediate
Portal Tooltip Component
Accessible tooltip rendered via Portal with automatic positioning and arrow indicator.
Best for: Icon button labels
#tooltip#portal
typescriptintermediate
Error Boundary with Fallback UI
Class-based error boundary component that catches render errors and displays a customizable fallback UI.
Best for: Graceful error recovery
#error-boundary#error-handling