With ssr:false, the sidebar rendered nothing in server HTML, so the content area took full width and page content (site name "Ciphera") appeared in the sidebar zone. Now the dynamic import has a loading placeholder — a 64px div with matching border/background that reserves the sidebar space in the server HTML. Content area never occupies the sidebar zone. Sidebar replaces the placeholder on client mount.
48 lines
1.3 KiB
TypeScript
48 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useCallback } from 'react'
|
|
import dynamic from 'next/dynamic'
|
|
import ContentHeader from './ContentHeader'
|
|
|
|
// Load sidebar only on the client — prevents SSR flash
|
|
const Sidebar = dynamic(() => import('./Sidebar'), {
|
|
ssr: false,
|
|
// Placeholder reserves the sidebar's space in the server HTML
|
|
// so page content never occupies the sidebar zone
|
|
loading: () => (
|
|
<div
|
|
className="hidden md:block shrink-0 border-r border-neutral-200/60 dark:border-neutral-800/60 bg-white/90 dark:bg-neutral-900/90 backdrop-blur-xl"
|
|
style={{ width: 64 }}
|
|
/>
|
|
),
|
|
})
|
|
|
|
export default function DashboardShell({
|
|
siteId,
|
|
children,
|
|
}: {
|
|
siteId: string
|
|
children: React.ReactNode
|
|
}) {
|
|
const [mobileOpen, setMobileOpen] = useState(false)
|
|
const closeMobile = useCallback(() => setMobileOpen(false), [])
|
|
const openMobile = useCallback(() => setMobileOpen(true), [])
|
|
|
|
return (
|
|
<div className="flex h-screen overflow-hidden">
|
|
<Sidebar
|
|
siteId={siteId}
|
|
mobileOpen={mobileOpen}
|
|
onMobileClose={closeMobile}
|
|
onMobileOpen={openMobile}
|
|
/>
|
|
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
|
<ContentHeader onMobileMenuOpen={openMobile} />
|
|
<main className="flex-1 overflow-y-auto pt-6">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|