React
Portals, modales y capas de interfaz
Explica cómo usar portals para renderizar modales y overlays fuera del contenedor visual, manteniendo el árbol React, eventos, focus y accesibilidad.
- Última actualización
- Actualizada
- Nivel
- Aplicación
React
Explica cómo usar portals para renderizar modales y overlays fuera del contenedor visual, manteniendo el árbol React, eventos, focus y accesibilidad.
createPortal renderiza DOM en otro contenedor sin cambiar la posición lógica del componente en el árbol React.
return createPortal(
<div role="dialog">...</div>,
document.body,
);Aunque el DOM aparezca fuera, el portal conserva Context y propagación según el árbol React.
overflow o stacking contexts.También necesitas:
Define una estrategia de capas en lugar de aumentar valores arbitrariamente. Los stacking contexts pueden impedir que un z-index alto funcione; un portal ayuda a cambiar el contenedor.
El contenedor DOM no existe en el servidor. Accede a él en un componente cliente y después del montaje cuando sea necesario.
Un portal cambia la ubicación física del DOM, no la relación lógica dentro de React.
Un portal pertenece a una posición del árbol React, pero su nodo aparece bajo otro contenedor DOM:
React tree
App
└─ Dialog
└─ Portal content
DOM tree
body
├─ #root
└─ #overlays
└─ dialogContext, state y propagación sintética siguen el árbol React. CSS, focus, stacking y relaciones DOM siguen la ubicación real.
<body>
<div id="root"></div>
<div id="overlays"></div>
</body>function Portal({ children }: { children: React.ReactNode }) {
const container = document.getElementById("overlays");
if (!container) throw new Error("Missing #overlays container");
return createPortal(children, container);
}No crees un contenedor nuevo durante cada render. Si el componente debe crearlo, haz setup/cleanup controlado y evita eliminar un nodo compartido por otros portals.
function Card() {
return (
<article onClick={openCard}>
<Portal>
<button onClick={closeDialog}>Cerrar</button>
</Portal>
</article>
);
}El click puede llegar al article por el árbol React aunque los nodos estén separados en DOM. Detén propagación cuando la semántica lo exija o mueve el portal a una posición React que represente mejor la relación. No uses stopPropagation para ocultar una estructura confusa.
Un modal necesita:
aria-labelledby o aria-label.aria-modal="true" cuando usa role dialog.Una primitive mantenida o <dialog> puede ayudar, pero verifica su comportamiento real.
No siempre enfoques el primer botón. Considera:
tabIndex={-1} para contenido largo.Guarda el elemento que abrió la capa y restaura si todavía existe.
Un trap debe manejar:
No implementes uno trivial con una lista capturada al montar; puede quedar obsoleto. Usa primitives probadas cuando el patrón es complejo.
function handleBackdropPointerDown(event: React.PointerEvent) {
if (event.target === event.currentTarget) onClose();
}Esto distingue el fondo del contenido. Decide si pointer down o click es correcto y evita cerrar al arrastrar desde dentro hacia fuera. No permitas backdrop close cuando se perderán datos críticos sin confirmación.
Solo la capa superior debería responder a Escape. Una estrategia de overlay manager puede administrar:
aria-hidden/inert del fondo.Valores de z-index arbitrarios por componente producen conflictos.
Un hijo con z-index: 9999 no escapa de un stacking context inferior. Causas:
transform.opacity.filter.isolation.Un portal mueve físicamente el nodo a una capa superior, pero no corrige un sistema de capas sin reglas.
No todo overlay es modal:
Elige role, focus y cierre según patrón, no solo por apariencia flotante.
Tooltips y menus necesitan medir anchor y viewport. Considera:
Una librería de positioning puede resolver geometría; sigue siendo tu responsabilidad aplicar semántica y lifecycle React.
Cambiar body.style.overflow requiere cleanup y coordinación entre overlays. Dos modales no deberían desbloquear scroll cuando uno sigue abierto. Un manager con contador o primitive puede administrar el recurso compartido.
El destino del portal no existe en servidor. Opciones:
No leas document durante server render.
Si el servidor no produjo el mismo portal, la primera renderización cliente puede diferir. Muchos frameworks manejan portals cliente después del montaje. Evita que el contenido crítico dependa exclusivamente de un modal abierto antes de hidratar.
Un modal de confirmación cliente no autoriza la operación. El servidor vuelve a validar. Protege contra clickjacking mediante headers de frame y evita HTML no sanitizado en overlays que parecen confiables.
Prueba:
z-index: 9999 puede no funcionar?useSyncExternalStore e integración con stores conecta React con fuentes externas sin romper consistencia concurrente.