From ae52d70a4e2eac8fa8bbfc3ce47076824f036e26 Mon Sep 17 00:00:00 2001 From: Anurag Yadav Date: Mon, 19 Oct 2026 22:54:40 +0530 Subject: [PATCH] FIxed hydration error Signed-off-by: Anurag Yadav --- components/shared/ThemeProvider.tsx | 65 +++++++++++++++++------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/components/shared/ThemeProvider.tsx b/components/shared/ThemeProvider.tsx index a234a39..0dc1c0a 100644 --- a/components/shared/ThemeProvider.tsx +++ b/components/shared/ThemeProvider.tsx @@ -1,6 +1,6 @@ "use client"; -import { createContext, useContext, useEffect, useState, useCallback } from "react"; +import { createContext, useContext, useCallback, useSyncExternalStore } from "react"; type Theme = "dark" | "light"; interface ThemeCtx { @@ -11,33 +11,46 @@ interface ThemeCtx { const Ctx = createContext(null); -export function ThemeProvider({ children }: { children: React.ReactNode }) { - // Seed from the attribute the pre-paint inline script already set (in - // app/layout.tsx), so React does not re-flip the theme after hydration. - const [theme, setThemeState] = useState(() => { - if (typeof document !== "undefined") { - const t = document.documentElement.dataset.theme; - if (t === "dark" || t === "light") return t; - } - return "dark"; +function getServerSnapshot(): Theme { + return "dark"; +} + +function subscribe(callback: () => void): () => void { + const obs = new MutationObserver(callback); + obs.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"], }); + return () => obs.disconnect(); +} + +function getClientSnapshot(): Theme { + const t = document.documentElement.dataset.theme; + return t === "light" ? "light" : "dark"; +} + +function applyTheme(t: Theme) { + const d = document.documentElement; + d.setAttribute("data-theme", t); + d.style.colorScheme = t; + try { + window.localStorage.setItem("cc:theme", t); + } catch { + /* ignore */ + } +} + +export function ThemeProvider({ children }: { children: React.ReactNode }) { + const theme = useSyncExternalStore(subscribe, getClientSnapshot, getServerSnapshot); + + const setTheme = useCallback((t: Theme) => applyTheme(t), []); + const toggle = useCallback(() => applyTheme(getClientSnapshot() === "dark" ? "light" : "dark"), []); - // Reflect on and keep the native color-scheme in sync. - useEffect(() => { - const d = document.documentElement; - d.setAttribute("data-theme", theme); - d.style.colorScheme = theme; - try { - window.localStorage.setItem("cc:theme", theme); - } catch { - /* ignore */ - } - }, [theme]); - - const setTheme = useCallback((t: Theme) => setThemeState(t), []); - const toggle = useCallback(() => setThemeState((t) => (t === "dark" ? "light" : "dark")), []); - - return {children}; + return ( + + {children} + + ); } export function useTheme(): ThemeCtx {