-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem
The root layout (src/app/layout.tsx) is currently a client component ('use client') that uses <Head> from next/head. This is incompatible with Next.js 13+ App Router and causes several issues:
- SSR Benefits Lost: Making the root layout a client component destroys server-side rendering benefits
- Metadata Not in Initial HTML: The
<Head>component fromnext/headdoesn't work properly in App Router - metadata like the RSS autodiscovery link isn't in the initial HTML, only added after client hydration - Anti-Pattern: App Router has a proper metadata API that should be used instead
- Discovery Issues: Search engines and RSS readers may not discover the RSS feed since the link isn't in the initial SSR HTML
Current Implementation
// src/app/layout.tsx
'use client' // ❌ Root layout should be server component
import Head from 'next/head' // ❌ Doesn't work properly in App Router
export default function RootLayout({ children }: { children: React.ReactNode }) {
const navRefs: NavRefsProps = NAV_ITEMS.reduce((acc: any, item) => {
acc[item.name] = useRef(null) // ❌ Client-side ref management
return acc
}, {})
return (
<html lang='en'>
<Head> // ❌ Should use metadata API instead
<title>Sean Oliver</title>
<meta property='og:title' content='Sean Oliver' key='title' />
<link rel='alternate' type='application/rss+xml' ... />
</Head>
<body>
<NavContext.Provider value={navRefs}> // ❌ Context requires client component
{children}
</NavContext.Provider>
</body>
</html>
)
}Root Cause
The layout is a client component because it needs:
useRef()hook for navigation scroll refsNavContext.Providerfor passing refs to navigation components
Proposed Solution
1. Convert Layout to Server Component
Move metadata to the proper App Router metadata API:
// src/app/layout.tsx (Server Component)
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Sean Oliver',
description: 'Growth Engineer at Supabase...',
openGraph: {
title: 'Sean Oliver',
// ...
},
alternates: {
types: {
'application/rss+xml': 'https://seanoliver.dev/feed.xml',
},
},
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='en'>
<body>
<NavProvider> {/* New client component wrapper */}
<Header />
<main>{children}</main>
<Footer />
</NavProvider>
</body>
</html>
)
}2. Create Client Component Wrapper for Navigation Context
// src/components/nav-provider.tsx
'use client'
import { useRef } from 'react'
import { NavContext } from '@/hooks/use-nav-context'
import { NAV_ITEMS } from '@/lib/constants'
import type { NavRefsProps } from '@/lib/types'
export function NavProvider({ children }: { children: React.ReactNode }) {
const navRefs: NavRefsProps = NAV_ITEMS.reduce((acc: any, item) => {
acc[item.name] = useRef(null)
return acc
}, {})
return <NavContext.Provider value={navRefs}>{children}</NavContext.Provider>
}3. Update Font Loading
Since fonts can't use useRef in server components:
// Keep font definitions at module level
const jetBrainsMono = localFont({ ... })
const monolisa = localFont({ ... })
// Apply via className instead of context
<html lang='en' className={clsx(jetBrainsMono.variable, monolisa.variable)}>Benefits
✅ Proper SSR: Root layout is server component, metadata in initial HTML
✅ Better SEO: Search engines see RSS autodiscovery link immediately
✅ Follows Best Practices: Uses App Router patterns correctly
✅ Improved Performance: Less JavaScript shipped to client
✅ Type Safety: Metadata API is fully typed
✅ Future Proof: Aligns with Next.js direction
Implementation Checklist
- Create
NavProviderclient component wrapper - Convert root layout to server component
- Move all
<Head>contents tometadataexport - Move font className application to html element
- Remove
'use client'directive from layout - Remove
next/headimport - Test navigation scroll behavior still works
- Test theme provider still works
- Verify RSS autodiscovery in view-source
- Test with JavaScript disabled (SSR should work)
Technical Notes
- The
ThemeProviderfromnext-themescan remain as a client component child - Navigation scroll functionality will continue working via
NavProvider - Font loading via
localFontworks in server components - This is a breaking change pattern but not a breaking change for users
References
Priority
Medium - This is an architectural improvement that should be done before the codebase grows larger. It doesn't break functionality but violates Next.js best practices and hurts SEO/discovery.