Part 28: Enhancing User Experience with Local Storage in Next.js
[Pages] Client-side Functionality

In our previous exploration of the Next.js framework, we discussed the concept of client-side hydration and how it brings interactivity to pre-rendered pages. Now, let's take it a step further by enhancing our "ThemeSwitch" component. Currently, users can toggle "Dark Mode," but this setting doesn't persist after a page reload. Let's implement a simple solution to remember user preferences using Local Storage.
Why Use Local Storage?
Local Storage is a web storage API that allows you to store key/value pairs in a user's browser. It's a convenient way to persist small amounts of data, such as user settings or preferences, without involving a server. By leveraging Local Storage, we can save the "darkMode" preference, ensuring a consistent user experience even after a page refresh.
Implementing Local Storage in ThemeSwitch
Saving the Dark Mode Preference
First, let's modify our "ThemeSwitch" component to save the "darkMode" state to Local Storage when the user toggles the theme. We'll create a separate function to handle the button click event.
// components/ThemeSwitch.js
import { useState } from 'react';
import DarkTheme from './DarkTheme';
function ThemeSwitch() {
const [darkMode, setDarkMode] = useState(false);
const handleClick = () => {
localStorage.setItem('darkMode', JSON.stringify(!darkMode));
setDarkMode(!darkMode);
};
const text = darkMode ? 'Light Mode' : 'Dark Mode';
return (
<>
<button onClick={handleClick}>
{text}
</button>
<style jsx>{`
button {
background: none;
border: none;
color: inherit;
cursor: pointer;
}
`}</style>
{darkMode && <DarkTheme />}
</>
);
}
export default ThemeSwitch;In the handleClick function, we use localStorage.setItem to save the toggled darkMode value as a string. JSON.stringify converts the boolean to a string, which is necessary because Local Storage stores data as strings.
Loading the Dark Mode Preference
Next, we need to ensure that the saved "darkMode" preference is loaded when the component initializes. We'll use a function to retrieve this value from Local Storage.
// components/ThemeSwitch.js
function loadDarkMode() {
const value = localStorage.getItem('darkMode');
return (value === null) ? false : JSON.parse(value);
}
function ThemeSwitch() {
const [darkMode, setDarkMode] = useState(loadDarkMode);
const handleClick = () => {
localStorage.setItem('darkMode', JSON.stringify(!darkMode));
setDarkMode(!darkMode);
};
const text = darkMode ? 'Light Mode' : 'Dark Mode';
return (
<>
<button onClick={handleClick}>
{text </button>
<style jsx>{`
button {
background: none;
border: none;
color: inherit;
cursor: pointer;
}
`}</style>
{darkMode && <DarkTheme />}
</>
);
}
export default ThemeSwitch;Here, we define loadDarkMode to check for the presence of a saved value in Local Storage. If a value exists, we parse it back to a boolean. Otherwise, we default to false, meaning "Dark Mode" is off by default.
Handling Server-Side Execution
When you run your Next.js app, some code might execute on the server, which doesn't have access to browser-specific APIs like localStorage. This can lead to errors during server-side rendering.
To address this, we can wrap our Local Storage logic in a check to ensure it only runs on the client side. Use the useEffect hook to perform client-side only operations, such as accessing Local Storage.
// components/ThemeSwitch.js
import { useState, useEffect } from 'react';
import DarkTheme from './DarkTheme';
function ThemeSwitch() {
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const value = localStorage.getItem('darkMode');
if (value !== null) {
setDarkMode(JSON.parse(value));
}
}, []);
const handleClick = () => {
localStorage.setItem('darkMode', JSON.stringify(!darkMode));
setDarkMode(!darkMode);
};
const text = darkMode ? 'Light Mode' : 'Dark Mode';
return (
<>
<button onClick={handleClick}>
{text}
</button>
<style jsx>{`
button {
background: none;
border: none;
color: inherit;
cursor: pointer;
}
`}</style>
{darkMode && <DarkTheme />}
</>
);
}
export default ThemeSwitch;With this setup, the useEffect hook runs only in the browser, ensuring that localStorage is accessed safely.
Conclusion
By integrating Local Storage with our "ThemeSwitch" component, we enhance user experience by preserving preferences across page reloads. This ensures that users return to their preferred settings without needing to toggle the theme each time. Understanding how to manage client-side storage efficiently is key to building responsive, user-friendly applications in Next.js.
Last updated