State
useReadLocalStorage
Read a localStorage (MDN, opens in a new tab) key without writing it, reactively.
Demo
live · imported from hookli
- useReadLocalStorage
- null
The hook never writes — it observes hookli-docs-theme and re-renders on storage and local-storage events, including from other tabs.
import { useReadLocalStorage } from "hookli";
export function Demo() {
const theme = useReadLocalStorage<string>("theme");
return <p>Saved theme: {theme ?? "none"}</p>;
}Usage
import { useReadLocalStorage } from "hookli";
export function Demo() {
const theme = useReadLocalStorage<string>("theme");
return <p>Saved theme: {theme ?? "none"}</p>;
}API
useReadLocalStorage<T>(key: string, options?: UseReadLocalStorageOptions<T>): T | nullParameters
| Name | Type | Default | Description |
|---|---|---|---|
| key | string | — | The localStorage key to observe. |
| options | UseReadLocalStorageOptions<T> | {} | Optional custom deserializer and hydration flag. |
Returns
| Name | Type | Description |
|---|---|---|
| value | T | null | The parsed value, or null when the key is absent. Re-renders when the key changes in another tab (storage event) or via a local-storage event dispatched in this tab. |
UseReadLocalStorageOptions<T>
Custom deserialization and hydration behaviour.
| Name | Type | Description |
|---|---|---|
| deserializer | (value: string) => T | Parse the stored string into a value. Defaults to JSON.parse, falling back to the raw string. |
| initializeWithValue | boolean | Read localStorage synchronously on mount. Set false to defer to after hydration and avoid SSR mismatches. |
Hook
The implementation, straight from hookli. Copy it, or install the package.
import { useCallback, useEffect, useState } from "react";
import { useEventListener } from "../use-event-listener/use-event-listener";
interface UseReadLocalStorageOptions<T> {
deserializer?: (value: string) => T;
initializeWithValue?: boolean;
}
const IS_SERVER = typeof window === "undefined";
export function useReadLocalStorage<T>(
key: string,
options: UseReadLocalStorageOptions<T> = {},
): T | null {
const { initializeWithValue = true } = options;
const deserializer = useCallback(
(value: string): T | undefined => {
if (options.deserializer) return options.deserializer(value);
if (value === "undefined") return undefined;
try {
return JSON.parse(value);
} catch {
return value as unknown as T;
}
},
[options],
);
const readValue = useCallback((): T | null => {
if (IS_SERVER) return null;
try {
const raw = window.localStorage.getItem(key);
return raw ? (deserializer(raw) as T) : null;
} catch {
return null;
}
}, [key, deserializer]);
const [storedValue, setStoredValue] = useState<T | null>(() =>
initializeWithValue ? readValue() : null,
);
useEffect(() => {
setStoredValue(readValue());
}, [key]);
const handleStorageChange = useCallback(
(event: StorageEvent) => {
if (event.key && event.key !== key) return;
setStoredValue(readValue());
},
[key, readValue],
);
useEventListener("storage", handleStorageChange);
useEventListener("local-storage", handleStorageChange);
return storedValue;
}