Overview
The useLocalStorage hook provides a React state hook that automatically synchronizes with browser localStorage. It handles JSON serialization, cross-tab synchronization, and error handling for localStorage operations.
Source: src/hooks/useLocalStorage.js
Hook Signature
export function useLocalStorage ( key , initialValue )
The localStorage key to store the value under
The default value if no stored value exists
Return Value
Returns a tuple similar to useState:
const [ value , setValue ] = useLocalStorage ( key , initialValue );
value : The current value (read from localStorage on mount)
setValue : Function to update the value (automatically saves to localStorage)
Features
Automatic persistence - Values are automatically saved to localStorage on change
Cross-tab synchronization - Changes in one tab are reflected in other tabs
JSON serialization - Automatically handles JSON stringify/parse
Error handling - Gracefully handles localStorage quota errors and private browsing mode
SSR safe - Checks for window availability before accessing localStorage
Usage Examples
Basic Usage
import { useLocalStorage } from './hooks/useLocalStorage' ;
function UserPreferences () {
const [ theme , setTheme ] = useLocalStorage ( 'theme' , 'light' );
return (
< button onClick = { () => setTheme ( theme === 'light' ? 'dark' : 'light' ) } >
Current theme: { theme }
</ button >
);
}
User Context Implementation
src/contexts/UserProvider.jsx
import { useLocalStorage } from '../hooks/useLocalStorage' ;
export default function UserProvider ({ children }) {
const [ user , setUser ] = useLocalStorage ( 'user' , null );
return (
< UserContext.Provider value = { { user , setUser } } >
{ children }
</ UserContext.Provider >
);
}
Chat Context Implementation
src/contexts/ChatProvider.jsx
import { useLocalStorage } from '../hooks/useLocalStorage' ;
export default function ChatProvider ({ children }) {
const [ currentChat , setCurrentChat ] = useLocalStorage ( 'chat' , null );
return (
< ChatContext.Provider value = { { currentChat , setCurrentChat } } >
{ children }
</ ChatContext.Provider >
);
}
Complex Objects
function UserSettings () {
const [ settings , setSettings ] = useLocalStorage ( 'settings' , {
notifications: true ,
theme: 'dark' ,
language: 'es'
});
const updateSetting = ( key , value ) => {
setSettings ( prev => ({
... prev ,
[key]: value
}));
};
return (
< div >
< label >
< input
type = "checkbox"
checked = { settings . notifications }
onChange = { ( e ) => updateSetting ( 'notifications' , e . target . checked ) }
/>
Enable notifications
</ label >
</ div >
);
}
Implementation Details
Initial Value from localStorage
On first render, the hook attempts to read from localStorage:
src/hooks/useLocalStorage.js:5-13
const [ value , setValue ] = useState (() => {
if ( typeof window === 'undefined' ) return initialValue
try {
const raw = window . localStorage . getItem ( key )
return raw ? JSON . parse ( raw ) : initialValue
} catch {
return initialValue
}
})
Automatic Persistence
Changes are automatically saved to localStorage:
src/hooks/useLocalStorage.js:15-19
useEffect (() => {
try {
window . localStorage . setItem ( key , JSON . stringify ( value ))
} catch { /* quota exceeded or private mode */ }
}, [ key , value ])
Cross-Tab Synchronization
Listens for storage events from other tabs:
src/hooks/useLocalStorage.js:21-29
useEffect (() => {
const onStorage = ( e ) => {
if ( e . key === key ) {
setValue ( e . newValue ? JSON . parse ( e . newValue ) : initialValue )
}
}
window . addEventListener ( 'storage' , onStorage )
return () => window . removeEventListener ( 'storage' , onStorage )
}, [ key , initialValue ])
Error Handling
The hook gracefully handles common localStorage errors:
Quota exceeded - Silently fails when storage limit is reached
Private browsing - Falls back to memory-only state
Parse errors - Returns initial value if stored data is corrupted
SSR - Returns initial value when window is undefined
Best Practices
Use descriptive, unique keys to avoid collisions: // Good
useLocalStorage ( 'ciclovital_user' , null )
// Avoid
useLocalStorage ( 'user' , null )
localStorage has a 5-10MB limit. Avoid storing large objects: // Good - store only essential user data
useLocalStorage ( 'user' , { id , email , name })
// Avoid - don't store large datasets
useLocalStorage ( 'all_messages' , largeMessageArray )
Provide sensible defaults
Always provide a default value that makes sense: const [ theme , setTheme ] = useLocalStorage ( 'theme' , 'theme-dark' );
const [ user , setUser ] = useLocalStorage ( 'user' , null );
const [ settings , setSettings ] = useLocalStorage ( 'settings' , {});
Security Considerations
Do not store sensitive information like passwords or tokens in localStorage. It is accessible to any JavaScript code on the page and is not encrypted.
// ❌ NEVER store sensitive data
useLocalStorage ( 'password' , '' );
useLocalStorage ( 'authToken' , '' );
// ✅ Only store non-sensitive user preferences
useLocalStorage ( 'theme' , 'dark' );
useLocalStorage ( 'language' , 'es' );
User Context Uses useLocalStorage for persistence
Chat Context Uses useLocalStorage for persistence
Theme Context Uses useLocalStorage for theme persistence
Settings Feature User guide for application settings