I am using next 13.1.0. I have a ContextProvider that sets a light and dark theme
'use client';
import { Theme, ThemeContext } from '@store/theme';
import { ReactNode, useState, useEffect } from 'react';
interface ContextProviderProps {
children: ReactNode
}
const ContextProvider = ({ children }: ContextProviderProps) => {
const [theme, setTheme] = useState<Theme>('dark');
useEffect(() => {
const storedTheme = localStorage.getItem('theme');
if (storedTheme === 'light' || storedTheme === 'dark') {
setTheme(storedTheme);
} else {
localStorage.setItem('theme', theme);
}
// added to body because of overscroll-behavior
document.body.classList.add(theme);
return () => {
document.body.classList.remove(theme);
};
}, [theme]);
const toggle = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
};
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
};
export { ContextProvider };
I use it in my root layout
import '@styles/globals.scss';
import { GlobalContent } from '@components/GlobalContent/GlobalContent';
import { ContextProvider } from '@components/ContextProvider/ContextProvider';
import { Inter } from '@next/font/google';
import { ReactNode } from 'react';
const inter = Inter({ subsets: ['latin'] });
interface RootLayoutProps {
children: ReactNode
}
const RootLayout = ({ children }: RootLayoutProps) => {
return (
<html lang="en" className={inter.className}>
<head />
<body>
<ContextProvider>
<GlobalContent>
{children}
</GlobalContent>
</ContextProvider>
</body>
</html>
);
};
export default RootLayout;
And I consume the theme value in my GlobalContent
'use client';
import styles from '@components/GlobalContent/GlobalContent.module.scss';
import { GlobalHeader } from '@components/GlobalHeader/GlobalHeader';
import { GlobalFooter } from '@components/GlobalFooter/GlobalFooter';
import { ThemeContext } from '@store/theme';
import { ReactNode, useContext } from 'react';
interface GlobalContentProps {
children: ReactNode
}
const GlobalContent = ({ children }: GlobalContentProps) => {
const { theme } = useContext(ThemeContext);
return (
<div className={`${theme === 'light' ? styles.lightTheme : styles.darkTheme}`}>
<GlobalHeader />
<div className={styles.globalWrapper}>
<main className={styles.childrenWrapper}>
{children}
</main>
<GlobalFooter />
</div>
</div>
);
};
export { GlobalContent };
I get the error
Hydration failed because the initial UI does not match what was rendered on the server.
React docs error link
I don't understand why I am getting this error because I am accessing localStorage
inside my useEffect
, so I expect the HTML generated on the server to be the same with the client before the first render.
How can I solve this error?
For Next.js 13, return jsx once the component is mounted
function Component() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return <></>;
// write rest of your code
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With