typescriptintermediate
Portal Dropdown Component
Dropdown menu rendered via Portal to avoid overflow clipping, with click-outside dismiss.
typescriptPress ⌘/Ctrl + Shift + C to copy
import { useState, useRef, useEffect, ReactNode } from 'react';
import { createPortal } from 'react-dom';
interface DropdownProps {
trigger: ReactNode;
children: ReactNode;
align?: 'left' | 'right';
}
export function PortalDropdown({ trigger, children, align = 'left' }: DropdownProps) {
const [open, setOpen] = useState(false);
const [pos, setPos] = useState({ top: 0, left: 0 });
const triggerRef = useRef<HTMLDivElement>(null);
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open || !triggerRef.current) return;
const rect = triggerRef.current.getBoundingClientRect();
setPos({
top: rect.bottom + window.scrollY + 4,
left: align === 'right' ? rect.right + window.scrollX : rect.left + window.scrollX,
});
}, [open, align]);
useEffect(() => {
if (!open) return;
const handler = (e: MouseEvent) => {
if (!menuRef.current?.contains(e.target as Node) && !triggerRef.current?.contains(e.target as Node)) {
setOpen(false);
}
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [open]);
return (
<>
<div ref={triggerRef} onClick={() => setOpen(o => !o)}>{trigger}</div>
{open && createPortal(
<div ref={menuRef} className="absolute bg-[#111] border border-white/10 rounded-lg shadow-xl p-2 z-50"
style={{ top: pos.top, left: pos.left, transform: align === 'right' ? 'translateX(-100%)' : undefined }}>
{children}
</div>,
document.body
)}
</>
);
}Use Cases
- Navigation menus
- Context actions
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
Portal-Based Modal Component
Accessible modal component using React portals with focus trapping, Escape key close, and backdrop click.
Best for: Confirmation dialogs
#modal#portal
typescriptintermediate
Portal Tooltip Component
Accessible tooltip rendered via Portal with automatic positioning and arrow indicator.
Best for: Icon button labels
#tooltip#portal
typescriptbeginner
useClickOutside — Outside Click Detection
Detect clicks outside a referenced element to close dropdowns, modals, and popover menus.
Best for: Dropdown menus
#hooks#click-outside
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