From 5e4fbe63e6f05bbd07d7eefcf4b819d6a94f0331 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sat, 2 May 2026 20:24:08 +0200 Subject: [PATCH] fix(login): render dev-login form client-only to avoid hydration mismatch Password manager extensions inject autofill icons / fill buttons into the input fields after SSR but before React hydration, causing a recoverable hydration mismatch. suppressHydrationWarning only covers attribute mismatches on a single element, not new injected children. Gate the form behind a useEffect-mounted flag so it renders client-only, sidestepping hydration entirely. The form is dev-only, so SSR was never useful here. --- src/app/login/dev-login-form.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/login/dev-login-form.tsx b/src/app/login/dev-login-form.tsx index 89a01a7..865cc4c 100644 --- a/src/app/login/dev-login-form.tsx +++ b/src/app/login/dev-login-form.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { signIn } from "next-auth/react"; type WorkspaceOption = { @@ -28,6 +28,12 @@ export function DevLoginForm({ workspaces }: { workspaces: WorkspaceOption[] }) const [workspaceId, setWorkspaceId] = useState(workspaces[0]?.id ?? ""); const [loading, setLoading] = useState(false); + // Render client-only — password manager extensions inject extra DOM nodes + // into the input fields below, which causes a hydration mismatch on SSR. + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + if (!mounted) return null; + async function handleLogin( loginEmail: string, loginName: string,