Portales para modales y tooltips
Use ReactDOM.createPortal para renderizar hijos en otro nodo del DOM, lo que permite crear modales y tooltips que escapan de contenedores overflow:hidden.
Portales para modales y tooltips es una lección gratuita de Frontend Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Frontend Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Frontend Academy incluye 4 lecciones en total.
El problema de escapar del contenedor
Los modales y los tooltips necesitan representarse en la parte superior del DOM para escapar de overflow:hidden, las capas de apilamiento de z-index y los contextos cuyo origen es un transform del padre. La solución son los portales.
API createPortal
createPortal(children, container) representa children dentro de container, cualquier nodo del DOM, y los mantiene lógicamente como hijos del componente React para conservar los eventos y el contexto.
import { createPortal } from 'react-dom';
function Modal({ children, onClose }) {
return createPortal(
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
}Contenedores de portales
Destinos habituales: document.body, un div específico #modal-root en index.html o un contenedor obtenido mediante una ref y creado cuando se necesita.
<!-- index.html -->
<div id="root"></div>
<div id="modal-root"></div>
// component:
const modalRoot = document.getElementById('modal-root');
return createPortal(<div>...</div>, modalRoot);¿Por qué no representarlo directamente?
Si un padre tiene overflow: hidden, el modal hijo queda recortado. Si un padre tiene transform: scale, el posicionamiento fijo del modal pasa a ser relativo al padre. Los portales evitan todos estos problemas.
Los eventos siguen propagándose
Aunque se representen en otra parte del DOM, los eventos de React se propagan por el árbol de componentes, no por el árbol del DOM. Un clic en el portal sigue propagándose hasta los controladores onClick de los componentes React ascendientes.
Cerrar al hacer clic en el overlay
Detenga la propagación del evento en el contenido del modal para que los clics en su interior no lo cierren.
function Modal({ onClose, children }) {
return createPortal(
<div className="backdrop" onClick={onClose}>
<div className="content" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
}Cerrar con la tecla Escape
Añada un listener de keydown mientras el modal esté abierto y elimínelo al desmontarlo.
useEffect(() => {
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [onClose]);Gestión del foco en portales
Cuando se abra un modal, mueva el foco a su interior, manténgalo allí hasta que se cierre y después devuélvalo al elemento que lo abrió. Use una biblioteca como focus-trap-react para evitar reimplementar esta lógica.
Elemento dialog nativo
El elemento <dialog> de HTML gestiona de forma nativa el posicionamiento del modal, el fondo superpuesto, la retención del foco y el cierre con Escape. Para la mayoría de los modales, es un punto de partida mejor que un portal.
function Modal({ open, onClose, children }) {
const ref = useRef(null);
useEffect(() => {
if (open) ref.current?.showModal();
else ref.current?.close();
}, [open]);
return (
<dialog ref={ref} onClose={onClose}>
{children}
</dialog>
);
}Tooltips y popovers
Los tooltips se posicionan con respecto a un elemento de anclaje. Un portal permite que escapen de los contextos de apilamiento. Combínelo con floating-ui para detectar colisiones, cambiar de lado y posicionar los elementos.
import { useFloating, autoUpdate } from '@floating-ui/react';
function Tooltip({ children, content }) {
const { refs, floatingStyles } = useFloating({ whileElementsMounted: autoUpdate });
const [open, setOpen] = useState(false);
return (
<>
<button ref={refs.setReference} onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
{children}
</button>
{open && createPortal(
<div ref={refs.setFloating} style={floatingStyles}>{content}</div>,
document.body
)}
</>
);
}Advertencia sobre SSR
document no existe en el servidor. Puede representar el contenido solo en el cliente, comprobando typeof window, o utilizar la opción dinámica de Next.js con ssr:false.
Accesibilidad: aria-modal
Los contenedores de los modales deben tener role="dialog", aria-modal="true" y aria-labelledby apuntando al encabezado del modal. Como alternativa, use <dialog> nativo.
Comprobación rápida
¿Por qué usar createPortal para los modales en lugar de representarlos directamente en el árbol de componentes?
Repaso: portales
createPortal(children, container) representa el DOM en otra ubicación, pero mantiene la estructura lógica del árbol de React. Resuelve los problemas causados por overflow, transform y z-index. Los eventos siguen propagándose por el árbol de React. Añada el cierre con Escape y gestione el foco. <dialog> nativo se encarga de gran parte de esto en los modales. Combínelo con floating-ui para los tooltips. No olvide SSR y la comprobación de typeof window.
Preguntas frecuentes
¿La lección «Portales para modales y tooltips» es gratis?
Sí — el texto completo de «Portales para modales y tooltips» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Frontend Academy, actualiza a CoddyKit PRO. El curso de Frontend Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Portales para modales y tooltips»?
Use ReactDOM.createPortal para renderizar hijos en otro nodo del DOM, lo que permite crear modales y tooltips que escapan de contenedores overflow:hidden. Practicas Frontend Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Frontend Academy?
No se requiere experiencia previa. Frontend Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Portales para modales y tooltips»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Frontend Academy?
Sí. Cada lección de Frontend Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Componentes compuestos con Context
- Render Props y patrones HOC
- Portales para modales y tooltips
- Límites de errores