From b37625007b06d8b01e89a8ac63b743ca9e067f0b Mon Sep 17 00:00:00 2001 From: Rami Bitar Date: Tue, 30 Jun 2026 17:26:30 -0400 Subject: [PATCH] Mount-gate DialogPortal for SSR safety Defensive guard so the portal only mounts client-side. Radix's Portal is already SSR-safe, so this is belt-and-suspenders. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_019RCfQKfr64YJbFRs3Ai9v2 --- components/ui/dialog.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index d9aecca..e77429e 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -22,6 +22,15 @@ function DialogTrigger({ function DialogPortal({ ...props }: React.ComponentProps) { + // Radix's Portal calls createPortal(children, document.body) as soon as it + // renders. During static prerendering there's no `document`, so gate the + // portal behind an effect that only runs in the browser. Closed dialogs + // render nothing on the server anyway, so there's no visual/hydration change. + const [mounted, setMounted] = React.useState(false) + React.useEffect(() => setMounted(true), []) + + if (!mounted) return null + return }