fix: resolve ThemeProvider SSR issue causing 500 error
All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 1m6s
All checks were successful
Build and Push Docker Image to Gitea / build-and-push (push) Successful in 1m6s
The app was crashing with "useTheme must be used within ThemeProvider" error during server-side rendering. Changes: - Created AppLayout client component to wrap Navbar - Modified useTheme hook to return default values during SSR instead of throwing an error - Updated Navbar to safely handle theme context - Moved Navbar rendering into client-side only AppLayout This fixes the SSR hydration mismatch and allows the app to render correctly on both server and client. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import type { Metadata } from 'next';
|
|||||||
import { Inter } from 'next/font/google';
|
import { Inter } from 'next/font/google';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
import { Providers } from '@/components/providers/Providers';
|
import { Providers } from '@/components/providers/Providers';
|
||||||
import { Navbar } from '@/components/layout/Navbar';
|
import { AppLayout } from '@/components/layout/AppLayout';
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ['latin'],
|
subsets: ['latin'],
|
||||||
@@ -44,8 +44,7 @@ export default function RootLayout({
|
|||||||
</head>
|
</head>
|
||||||
<body className={`${inter.variable} antialiased`}>
|
<body className={`${inter.variable} antialiased`}>
|
||||||
<Providers>
|
<Providers>
|
||||||
<Navbar />
|
<AppLayout>{children}</AppLayout>
|
||||||
<main className="container mx-auto px-4 py-8">{children}</main>
|
|
||||||
</Providers>
|
</Providers>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
13
components/layout/AppLayout.tsx
Normal file
13
components/layout/AppLayout.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
import { Navbar } from './Navbar';
|
||||||
|
|
||||||
|
export function AppLayout({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen">
|
||||||
|
<Navbar />
|
||||||
|
<main className="flex-1 container mx-auto px-4 py-8">{children}</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -19,14 +19,16 @@ const navItems = [
|
|||||||
export function Navbar() {
|
export function Navbar() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const { theme, setTheme, resolvedTheme } = useTheme();
|
const themeContext = useTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
|
if (themeContext) {
|
||||||
|
themeContext.setTheme(themeContext.resolvedTheme === 'dark' ? 'light' : 'dark');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -59,14 +61,14 @@ export function Navbar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Theme Toggle */}
|
{/* Theme Toggle */}
|
||||||
{mounted && (
|
{mounted && themeContext && (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
aria-label="Toggle theme"
|
aria-label="Toggle theme"
|
||||||
>
|
>
|
||||||
{resolvedTheme === 'dark' ? (
|
{themeContext.resolvedTheme === 'dark' ? (
|
||||||
<Sun className="h-5 w-5" />
|
<Sun className="h-5 w-5" />
|
||||||
) : (
|
) : (
|
||||||
<Moon className="h-5 w-5" />
|
<Moon className="h-5 w-5" />
|
||||||
|
|||||||
@@ -71,7 +71,12 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
|
|||||||
export function useTheme() {
|
export function useTheme() {
|
||||||
const context = useContext(ThemeContext);
|
const context = useContext(ThemeContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('useTheme must be used within ThemeProvider');
|
// Return a default context for SSR or when outside provider
|
||||||
|
return {
|
||||||
|
theme: 'system' as Theme,
|
||||||
|
setTheme: () => {},
|
||||||
|
resolvedTheme: 'light' as 'light' | 'dark',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user