From ef800eee40c9b320637a11e0249fa0d0e83ea86e Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 19 May 2026 19:58:21 -0300 Subject: [PATCH] fix(offline): avoid SSR/CSR hydration mismatch on navigator.onLine Replace useState+lazy-initializer with useSyncExternalStore so the server snapshot (() => false) and client snapshot (() => navigator.onLine) are declared separately. React hydrates with the server value and switches to the real online status client-side without a mismatch. --- src/app/offline/page.tsx | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/app/offline/page.tsx b/src/app/offline/page.tsx index 045b254fc7..a280f45814 100644 --- a/src/app/offline/page.tsx +++ b/src/app/offline/page.tsx @@ -1,25 +1,24 @@ "use client"; -import { useEffect, useState } from "react"; +import { useSyncExternalStore } from "react"; import Link from "next/link"; +function subscribeToOnline(callback: () => void) { + window.addEventListener("online", callback); + window.addEventListener("offline", callback); + return () => { + window.removeEventListener("online", callback); + window.removeEventListener("offline", callback); + }; +} + export default function OfflinePage() { - const [isOnline, setIsOnline] = useState(() => - typeof navigator !== "undefined" ? navigator.onLine : true + const isOnline = useSyncExternalStore( + subscribeToOnline, + () => navigator.onLine, + () => false ); - useEffect(() => { - const onOnline = () => setIsOnline(true); - const onOffline = () => setIsOnline(false); - - window.addEventListener("online", onOnline); - window.addEventListener("offline", onOffline); - return () => { - window.removeEventListener("online", onOnline); - window.removeEventListener("offline", onOffline); - }; - }, []); - return (