'How do I persist my theme to localStorage in NextJs?
I have my theme stored in a stateful function that defaults to false. I then passed a onclick function to toggle between false and true. I want that stored in localstorage and to persist on refresh. But i can't access local storage in nextJS as the initial value.
const [dark, setDark] = useState(false);
useEffect(() => {
localStorage.setItem("theme", JSON.stringify(dark));
}, [dark]);
useEffect(() => {
const theme = JSON.parse(localStorage.getItem("theme"));
if (theme) {
setDark(theme);
}
}, []);
function setTheme() {
setDark(!dark);
}
Solution 1:[1]
There is very famous library with will help to do that with redux.
https://www.npmjs.com/package/redux-persist
You can simply put your theme and pass that reducer to redux persists whitelist. It will help to persist the theme as well as. Single source of truth Concept in React System will also be satisfied.
const persistConfig = { key: 'root', storage: storage, whitelist: ['navigation'] // only navigation Reducer will be persisted };
Solution 2:[2]
Next.js is a server-side rendering framework which means the initial values are from the server. You cannot access local storage from the server until you landed on the client-side.
If you want to have theme data from the server-side you can use nookies
which is cookies helpers for NextJs
https://github.com/maticzav/nookies
Your logic maybe like this
useEffect(() => {
nookies.set({},"theme", JSON.stringify(dark));
}, [dark]);
useEffect(() => {
const {theme} = nookies.get({}));
if (theme) {
setDark(theme);
}
}, []);
Solution 3:[3]
You can use localstorage in Next js inside useEffect hook as it is called only on the client side.
So the localstorage is accessible in useEffect.
I personally don't use libraries for this. Here's how you can do it.
- A handler to toggle the theme.
- A handler to check theme from localStorage when App component mounts or theme changes.
Use a themeToggler handler in the Navbar component for the theme button.
_app.js
export default function App({Component, pageProps}){
const [darkMode, setDarkMode] = useState(false);
// check and reset theme when `darkMode` changes
useEffect(() => {
themeCheck();
}, [darkMode]);
// check theme on component mount
useEffect(() => {
themeCheck();
}, []);
// check and reset theme
const themeCheck = () => {
if (
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
setDarkMode(true);
} else {
document.documentElement.classList.remove("dark");
setDarkMode(false);
}
}
return (
<>
<Navbar darkMode={darkMode} setDarkMode={setDarkMode}/>
<Component {...pageProps} />
</>
);
Navbar.js
export default function Navbar({ darkMode, setDarkMode }) {
// called when theme button is pressed
const toggleTheme = () => {
const theme = localStorage.getItem("theme");
if (theme) {
localStorage.setItem("theme", theme === "dark" ? "light" : "dark");
} else {
localStorage.setItem("theme", "dark");
}
setDarkMode(!darkMode);
};
return (
<>
<button id="themeBtn" onClick={toggleTheme}>
Change theme
</button>
</>
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Diptom Saha |
Solution 2 | |
Solution 3 | Amit |