Keeping UI state after a page refresh can seriously boost user experience. But dragging in Redux Persist or localforage is often unnecessary for small to mid-sized apps. Here’s a no-library method to persist React component state across reloads using just the browser’s native capabilities.
Why Bother With State Persistence?
Use cases include:
- Keeping form inputs intact after accidental reloads
- Saving UI preferences like dark mode toggles
- Storing lightweight user session data
Step 1: Build a usePersistentState Hook
This custom hook wraps useState
and connects it to localStorage
automatically:
// usePersistentState.js
import { useState, useEffect } from "react";
export function usePersistentState(key, defaultValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored !== null ? JSON.parse(stored) : defaultValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
Step 2: Use It in Your Components
// SettingsForm.js
import { usePersistentState } from "./usePersistentState";
function SettingsForm() {
const [username, setUsername] = usePersistentState("username", "");
const [theme, setTheme] = usePersistentState("theme", "light");
return (
<form>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Enter your username"
/>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</form>
);
}
export default SettingsForm;
Step 3: That's It
Now even if the user refreshes the page, the input fields and settings stick without server involvement.
Pros and Cons
✅ Pros
- No external dependencies
- Instant, seamless persistence for critical UI elements
- Can be abstracted for sessionStorage, cookies, or IndexedDB if needed
⚠️ Cons
- localStorage is synchronous — heavy writes could block the main thread (rare)
- Data must be JSON-serializable (no functions, Dates need special handling)
- localStorage is shared across tabs — no "tab-scoped" persistence
🚀 Alternatives
- Redux Persist: If you're already using Redux, it handles complex state trees well.
- Jotai + atomWithStorage: Dead-simple for smaller apps with better reactivity.
Summary
Sometimes you don't need a sledgehammer for a thumbtack. For lightweight state persistence in React, a simple hook backed by localStorage is fast, free, and robust enough for tons of real-world use cases.
For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:
Using React Portals Like a Pro.
If you found this useful, you can support me here: buymeacoffee.com/hexshift