diff --git a/components/dashboard/DashboardShell.tsx b/components/dashboard/DashboardShell.tsx index c0cd867..4f744c6 100644 --- a/components/dashboard/DashboardShell.tsx +++ b/components/dashboard/DashboardShell.tsx @@ -1,9 +1,12 @@ 'use client' import { useState, useCallback } from 'react' -import Sidebar from './Sidebar' +import dynamic from 'next/dynamic' import ContentHeader from './ContentHeader' +// Load sidebar only on the client — prevents any SSR flash of text/labels +const Sidebar = dynamic(() => import('./Sidebar'), { ssr: false }) + export default function DashboardShell({ siteId, children, diff --git a/components/dashboard/Sidebar.tsx b/components/dashboard/Sidebar.tsx index b566573..abd8fc7 100644 --- a/components/dashboard/Sidebar.tsx +++ b/components/dashboard/Sidebar.tsx @@ -240,16 +240,10 @@ export default function Sidebar({ const [sites, setSites] = useState([]) const [pendingHref, setPendingHref] = useState(null) const wasCollapsedRef = useRef(false) - const [ready, setReady] = useState(false) - const [collapsed, setCollapsed] = useState(true) - - // Read saved state and reveal sidebar in one frame — no flash - useEffect(() => { - const saved = localStorage.getItem(SIDEBAR_KEY) - if (saved === 'false') setCollapsed(false) - // Reveal after state is set — React batches these, so sidebar appears at correct width - requestAnimationFrame(() => setReady(true)) - }, []) + // Safe to read localStorage directly — this component is loaded with ssr:false + const [collapsed, setCollapsed] = useState(() => { + return localStorage.getItem(SIDEBAR_KEY) !== 'false' + }) useEffect(() => { listSites().then(setSites).catch(() => {}) }, []) useEffect(() => { setPendingHref(null); onMobileClose() }, [pathname, onMobileClose]) @@ -340,17 +334,13 @@ export default function Sidebar({ return ( <> - {/* Desktop — empty shell until ready, then real content */} - {!ready ? ( -
- ) : ( - - )} + {/* Desktop — ssr:false means this only renders on client, no hydration flash */} + {/* Mobile overlay */} {mobileOpen && (