From 856e577f4b2973c75fa1ce59ba2b3fa3355cb5e9 Mon Sep 17 00:00:00 2001 From: egecankomur Date: Thu, 14 May 2026 20:03:41 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20server-side=20UA=20mobile=20detection=20?= =?UTF-8?q?=E2=80=94=20prevents=20desktop=20sidebar=20flash=20on=20mobile?= =?UTF-8?q?=20before=20JS=20hydration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(dashboard)/dashboard-shell.tsx | 4 ++++ src/app/(dashboard)/layout.tsx | 11 ++++++++++- src/components/ui/sidebar.tsx | 4 +++- src/hooks/use-mobile.ts | 26 ++++++++----------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/app/(dashboard)/dashboard-shell.tsx b/src/app/(dashboard)/dashboard-shell.tsx index aa54db6..34eda14 100644 --- a/src/app/(dashboard)/dashboard-shell.tsx +++ b/src/app/(dashboard)/dashboard-shell.tsx @@ -33,6 +33,7 @@ export function DashboardShell({ initialPrefs, pendingMatchCount = 0, role = "member", + serverIsMobile = false, }: { user: ShellUser; company: ShellCompany; @@ -40,6 +41,7 @@ export function DashboardShell({ initialPrefs: ThemePrefs; pendingMatchCount?: number; role?: ShellRole; + serverIsMobile?: boolean; }) { const [themeCustomizerOpen, setThemeCustomizerOpen] = React.useState(false); const { config } = useSidebarConfig(); @@ -47,6 +49,8 @@ export function DashboardShell({ return ( + {children} ); diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index 1d89709..0d8737f 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -54,6 +54,7 @@ function useSidebar() { function SidebarProvider({ defaultOpen = true, + defaultIsMobile = false, open: openProp, onOpenChange: setOpenProp, className, @@ -62,10 +63,11 @@ function SidebarProvider({ ...props }: React.ComponentProps<"div"> & { defaultOpen?: boolean + defaultIsMobile?: boolean open?: boolean onOpenChange?: (open: boolean) => void }) { - const isMobile = useIsMobile() + const isMobile = useIsMobile(defaultIsMobile) const [openMobile, setOpenMobile] = React.useState(false) // This is the internal state of the sidebar. diff --git a/src/hooks/use-mobile.ts b/src/hooks/use-mobile.ts index 2d0e7fe..5bc4162 100644 --- a/src/hooks/use-mobile.ts +++ b/src/hooks/use-mobile.ts @@ -2,26 +2,16 @@ import * as React from "react" const MOBILE_BREAKPOINT = 768 -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState(undefined) +export function useIsMobile(defaultValue = false) { + const [isMobile, setIsMobile] = React.useState(defaultValue) React.useEffect(() => { - const mql = typeof window !== "undefined" ? window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) : null - const onChange = () => { - setIsMobile(typeof window !== "undefined" ? window.innerWidth < MOBILE_BREAKPOINT : false) - } - - if (mql) { - mql.addEventListener("change", onChange) - } - setIsMobile(typeof window !== "undefined" ? window.innerWidth < MOBILE_BREAKPOINT : false) - - return () => { - if (mql) { - mql.removeEventListener("change", onChange) - } - } + const update = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) + mql.addEventListener("change", update) + update() + return () => mql.removeEventListener("change", update) }, []) - return !!isMobile + return isMobile }