From 89c20e06a070e719cf47816300683c2eb33c5c5f Mon Sep 17 00:00:00 2001 From: omoh5 Date: Thu, 30 Jul 2026 07:25:53 +0000 Subject: [PATCH] fix(frontend): guard against SSR/hydration mismatch in theme provider (#1083) Add blocking inline script in that reads localStorage theme preference before first paint, preventing flash-of-wrong-theme when client theme differs from server-rendered default. Add comprehensive JSDoc comments documenting the SSR/hydration strategy in both theme-provider.tsx and layout.tsx. --- frontend/src/app/layout.tsx | 45 +++++++++++++++++++++-- frontend/src/context/theme-provider.tsx | 47 +++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index e039ba12..f063814f 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -59,9 +59,50 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode; }>) { + // suppressHydrationWarning allows the blocking inline script in to + // mutate the `dark` class on without React logging a hydration + // mismatch warning. See theme-provider.tsx for the full strategy. return ( - {/* TODO: revisit if i18n is introduced */} - + + + {/* + Blocking inline script that reads the user's stored theme preference + from localStorage and applies the `dark` class to BEFORE the + browser paints a single pixel. This prevents the flash-of-wrong-theme + (FART) when the server renders a default theme class that differs from + the user's persisted preference. + + The `suppressHydrationWarning` prop on is the companion measure: + after this script mutates the class, React will see a mismatch between + server-rendered (no `dark` class) and the client DOM (possibly + `dark` class) and would normally emit a console warning. + suppressHydrationWarning tells React to skip that check for this + element because the discrepancy is intentional and resolved before + hydration completes. + + Flow: inline script runs → class set → browser paints → React hydrates + (suppressHydrationWarning) → next-themes ThemeProvider takes over. + */} +