useReadLocalStorage
React hook that reads a value from localStorage with automatic updates, cross-tab synchronization, and SSR compatibility.
Installation
npx shadcn@latest add https://www.shadcn.io/registry/use-read-local-storage.json
npx shadcn@latest add https://www.shadcn.io/registry/use-read-local-storage.json
pnpm dlx shadcn@latest add https://www.shadcn.io/registry/use-read-local-storage.json
bunx shadcn@latest add https://www.shadcn.io/registry/use-read-local-storage.json
Features
- Read-only access - Lightweight alternative to useLocalStorage for read-only scenarios
- Automatic updates - Responds to localStorage changes across tabs and windows
- SSR compatible - Optional initialization for server-side rendering
- Custom deserialization - Support for custom deserializer functions
- Type safety - Full TypeScript support with proper generics
- Error handling - Graceful fallbacks for localStorage errors
- Cross-tab sync - Updates when localStorage changes in other tabs
Usage
import { useReadLocalStorage } from "@/hooks/use-read-local-storage"
function UserProfile() {
// Assuming a value was set in localStorage with this key
const userPreferences = useReadLocalStorage<{ theme: string; lang: string }>("user-prefs")
return (
<div>
{userPreferences && (
<>
<p>Theme: {userPreferences.theme}</p>
<p>Language: {userPreferences.lang}</p>
</>
)}
</div>
)
}
API Reference
useReadLocalStorage
// Client-side version
useReadLocalStorage<T>(key: string, options?: Partial<Options<T, true>>): T | null
// SSR version
useReadLocalStorage<T>(key: string, options: Options<T, false>): T | null | undefined
Options
Property | Type | Default | Description |
---|---|---|---|
deserializer | (value: string) => T | JSON.parse | Custom function to deserialize the stored string value |
initializeWithValue | boolean | true | Whether to initialize with localStorage value (set to false for SSR) |
Return Value
T | null
- The stored value ornull
if key doesn't exist or error occursT | null | undefined
- WheninitializeWithValue: false
, may returnundefined
during SSR
Usage Examples
Basic Reading
const darkMode = useReadLocalStorage<boolean>("darkMode")
const username = useReadLocalStorage<string>("username")
const settings = useReadLocalStorage<{ notifications: boolean }>("settings")
return (
<div className={darkMode ? "dark" : "light"}>
<p>Welcome, {username || "Guest"}</p>
<p>Notifications: {settings?.notifications ? "On" : "Off"}</p>
</div>
)
Custom Deserializer
const lastVisit = useReadLocalStorage<Date>("lastVisit", {
deserializer: (value) => new Date(value)
})
return (
<div>
{lastVisit && <p>Last visit: {lastVisit.toLocaleDateString()}</p>}
</div>
)
SSR Compatible
const theme = useReadLocalStorage<string>("theme", {
initializeWithValue: false
})
// During SSR, theme will be undefined
// After hydration, theme will be the stored value or null
return (
<div className={theme || "default-theme"}>
Content
</div>
)
Reading User Preferences
function AppSettings() {
const userSettings = useReadLocalStorage<{
theme: "light" | "dark"
language: string
notifications: boolean
autoSave: boolean
}>("app-settings")
if (!userSettings) {
return <div>Loading user settings...</div>
}
return (
<div>
<h2>Current Settings</h2>
<ul>
<li>Theme: {userSettings.theme}</li>
<li>Language: {userSettings.language}</li>
<li>Notifications: {userSettings.notifications ? "Enabled" : "Disabled"}</li>
<li>Auto-save: {userSettings.autoSave ? "On" : "Off"}</li>
</ul>
</div>
)
}
Watching Shopping Cart
function CartIndicator() {
const cartItems = useReadLocalStorage<Array<{ id: string; name: string; qty: number }>>("cart-items")
const totalItems = cartItems?.reduce((sum, item) => sum + item.qty, 0) || 0
return (
<button>
🛒 Cart ({totalItems})
</button>
)
}
Reading Authentication Token
function AuthStatus() {
const token = useReadLocalStorage<string>("auth-token")
const user = useReadLocalStorage<{ name: string; email: string }>("user-data")
return (
<div>
{token ? (
<div>
<p>✅ Logged in as {user?.name}</p>
<p>Email: {user?.email}</p>
</div>
) : (
<p>❌ Not logged in</p>
)}
</div>
)
}
Multiple Storage Keys
function Dashboard() {
const theme = useReadLocalStorage<string>("theme")
const layout = useReadLocalStorage<"grid" | "list">("dashboard-layout")
const filters = useReadLocalStorage<string[]>("active-filters")
const lastSync = useReadLocalStorage<Date>("last-sync", {
deserializer: (value) => new Date(value)
})
return (
<div className={`theme-${theme || "default"}`}>
<div className={layout === "grid" ? "grid-layout" : "list-layout"}>
<p>Active filters: {filters?.join(", ") || "None"}</p>
<p>Last sync: {lastSync?.toLocaleString() || "Never"}</p>
</div>
</div>
)
}
Common Use Cases
- Reading user preferences - Theme, language, layout settings
- Authentication state - Reading stored tokens or user data
- Shopping cart display - Showing cart item count from localStorage
- Feature flags - Reading enabled/disabled features
- Recently viewed items - Displaying browsing history
- Form auto-save - Reading saved draft content
- Analytics settings - Reading consent and tracking preferences
- App configuration - Reading stored configuration options
Comparison with useLocalStorage
Feature | useReadLocalStorage | useLocalStorage |
---|---|---|
Read values | ✅ | ✅ |
Write values | ❌ | ✅ |
Bundle size | Smaller | Larger |
Use case | Read-only scenarios | Read/write scenarios |
Performance | Optimized for reading | General purpose |
Implementation Details
- Uses
useEventListener
to listen for bothstorage
and customlocal-storage
events - Automatically updates when localStorage changes in the current tab or other tabs
- Uses
JSON.parse
by default but supports custom deserializer functions - Handles localStorage errors gracefully by returning
null
- Supports server-side rendering with
initializeWithValue: false
- Re-reads value whenever the key changes
- Provides type safety with TypeScript generics
Best Practices
- Use for read-only scenarios: When you only need to read localStorage values
- Handle null values: Always check if the returned value is null
- Provide fallbacks: Use default values when localStorage is empty
- Custom deserializers: Use for complex data types like dates
- SSR considerations: Set
initializeWithValue: false
for server-side rendering - Type safety: Always specify the generic type for better TypeScript support
useOnClickOutside
Handle clicks outside specified elements with support for multiple refs and event types. Perfect for React applications requiring modal and dropdown behavior with Next.js integration and TypeScript support.
useResizeObserver
Custom hook that observes the size of an element using the ResizeObserver API. Perfect for responsive React applications requiring dynamic layout adjustments with Next.js integration and TypeScript support.