Navbar & Footer

Auth-aware Navbar with mobile hamburger, dark mode toggle, dynamic NavLink array, and CTA slot. Server-rendered Footer with dynamic link groups.

PropTypeDefaultDescription
linksNavLink[]requiredNavigation links - rendered in desktop nav and mobile menu
ctaSlotReactNodeundefinedRight-side CTA - e.g. 'Get started' button
logoReactNodeundefinedBrand logo or text - renders left of nav
showThemeTogglebooleantrueWhether to show the dark/light mode toggle
showAuthbooleantrueWhether to show session-aware login/logout

NavLink type from types/index.ts:

type NavLink = {
  label: string
  href: string
  icon?: string       // lucide icon name - optional
  external?: boolean  // opens in _blank with rel noopener noreferrer
}

const links: NavLink[] = [
  { label: 'Dashboard', href: '/dashboard' },
  { label: 'Settings', href: '/settings' },
  { label: 'GitHub', href: 'https://github.com', external: true },
]

Auth awareness

Navbar uses useSession() from next-auth/react:

  • status === 'loading': shows skeleton placeholder - no layout shift
  • session exists: shows user name/avatar + logout button (calls logoutAction)
  • no session: shows Login and Register links

Dark mode toggle

ThemeToggle reads from and writes to localStorage under the 'theme' key. It toggles the 'dark' class on document.documentElement.

On first render, uses the mounted pattern to avoid SSR hydration mismatch - renders nothing until mounted, then reads localStorage or system preference.

PropTypeDefaultDescription
linksNavLink[][]undefinedArray of link groups - each inner array is one column
copyrightNamestringundefinedName used in '© 2025 [name]'
logoReactNodeundefinedBrand mark in footer

Usage

app/layout.tsx
import Navbar from '@/components/nav/Navbar'
import Footer from '@/components/footer/Footer'
import Button from '@/components/ui/Button'

const navLinks = [
  { label: 'Dashboard', href: '/dashboard' },
  { label: 'Settings', href: '/settings' },
]

const footerLinks = [
  [
    { label: 'Dashboard', href: '/dashboard' },
    { label: 'Settings', href: '/settings' },
  ],
  [
    { label: 'GitHub', href: 'https://github.com', external: true },
    { label: 'Docs', href: '/docs' },
  ],
]

export default function Layout({ children }) {
  return (
    <>
      <Navbar
        links={navLinks}
        ctaSlot={<Button size="sm">Upgrade</Button>}
      />
      <main>{children}</main>
      <Footer links={footerLinks} copyrightName="MyApp" />
    </>
  )
}