From 4f20ff33ae8aeaf0695006ef9ae1d00080565009 Mon Sep 17 00:00:00 2001 From: LucasDLTG Date: Tue, 28 Apr 2026 10:26:33 +0200 Subject: [PATCH] fix: ssr rendering --- src/components/layout/LocaleSwitcher.tsx | 1 + src/hooks/useTheme.ts | 94 ++++++++++++++---------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/components/layout/LocaleSwitcher.tsx b/src/components/layout/LocaleSwitcher.tsx index 0a300d5..1e2a8d3 100644 --- a/src/components/layout/LocaleSwitcher.tsx +++ b/src/components/layout/LocaleSwitcher.tsx @@ -19,6 +19,7 @@ export function LocaleSwitcher() { return (
+ {/* eslint-disable-next-line @next/next/no-img-element */} {currentLocale} void>(); +let clientTheme: Theme | null = null; // lazily initialised on first client read + +function getSnapshot(): Theme { + if (clientTheme === null) { + clientTheme = readThemeFromDOM(); + } + return clientTheme; +} + +// The server always returns the default theme so SSR HTML is deterministic. +// The client will then reconcile with the real cookie/DOM value via getSnapshot. +function getServerSnapshot(): Theme { + return 'dark'; +} + +function subscribe(callback: () => void): () => void { + listeners.add(callback); + return () => listeners.delete(callback); +} + +function setGlobalTheme(next: Theme) { + clientTheme = next; + applyThemeToDOM(next); + setCookie(next); + listeners.forEach((cb) => cb()); +} + +// ── Hook ────────────────────────────────────────────────────────────────────── + export function useTheme() { - const [theme, setTheme] = useState('dark'); + const theme = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); - useEffect(() => { - const cookieTheme = getCookie('theme') as Theme | null; - const rootTheme = document.documentElement.classList.contains('dark') - ? 'dark' - : 'light'; - - const initial = cookieTheme || rootTheme || 'dark'; - - setTheme(initial); - - document.documentElement.classList.toggle('dark', initial === 'dark'); - document.documentElement.classList.toggle( - 'light-mode', - initial === 'light', - ); - }, []); - - function applyTheme(t: Theme) { - document.documentElement.classList.toggle('dark', t === 'dark'); - document.documentElement.classList.toggle('light-mode', t === 'light'); - - setCookie(t); - } - - function toggleTheme() { - setTheme((prev) => { - const next = prev === 'dark' ? 'light' : 'dark'; - applyTheme(next); - return next; - }); - } + const toggleTheme = useCallback(() => { + setGlobalTheme(theme === 'dark' ? 'light' : 'dark'); + }, [theme]); return { theme, dark: theme === 'dark', toggleTheme, }; -} +} \ No newline at end of file