useLocalStorage
React hook to store, retrieve and delete data from the browser's localStorage API.
Add the hook via the CLI:
sh
npx @novajslabs/cli add useLocalStorage
sh
npx @novajslabs/cli add useLocalStorage
sh
pnpm dlx @novajslabs/cli add useLocalStorage
Or copy and paste the code into your project:
ts
import { useEffect, useSyncExternalStore, useCallback } from "react";
const isFunction = <T>(
value: T | ((prevState: T) => T)
): value is (prevState: T) => T => typeof value === "function";
const dispatchStorageEvent = (key: string, newValue: string | null) =>
window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
const getLocalStorageItem = (key: string) => window.localStorage.getItem(key);
const setLocalStorageItem = <T>(key: string, value: T) => {
const stringifiedValue = JSON.stringify(value);
window.localStorage.setItem(key, stringifiedValue);
dispatchStorageEvent(key, stringifiedValue);
};
const removeLocalStorageItem = (key: string) => {
window.localStorage.removeItem(key);
dispatchStorageEvent(key, null);
};
const localStorageSubscribe = (cb: () => void) => {
window.addEventListener("storage", cb);
return () => window.removeEventListener("storage", cb);
};
export const useLocalStorage = <T>(key: string, initialValue: T) => {
const getSnapshot = () => getLocalStorageItem(key);
const store = useSyncExternalStore(localStorageSubscribe, getSnapshot);
const setState = useCallback(
(v: T) => {
try {
let nextState: T;
if (isFunction(v)) {
const parsedStore = store ? JSON.parse(store) : null;
nextState = v(parsedStore ?? initialValue);
} else {
nextState = v;
}
if (nextState === undefined || nextState === null) {
removeLocalStorageItem(key);
} else {
setLocalStorageItem(key, nextState);
}
} catch (e) {
console.log(e);
}
},
[key, store, initialValue]
);
useEffect(() => {
if (
getLocalStorageItem(key) === null &&
typeof initialValue !== "undefined"
) {
setLocalStorageItem(key, initialValue);
}
}, [key, initialValue]);
return {
current: store ? JSON.parse(store) : initialValue,
setItemValue: setState,
removeItem: () => removeLocalStorageItem(key),
};
};
js
import { useEffect, useSyncExternalStore, useCallback } from "react";
const isFunction = (value) => typeof value === "function";
const dispatchStorageEvent = (key, newValue) =>
window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
const getLocalStorageItem = (key) => window.localStorage.getItem(key);
const setLocalStorageItem = (key, value) => {
const stringifiedValue = JSON.stringify(value);
window.localStorage.setItem(key, stringifiedValue);
dispatchStorageEvent(key, stringifiedValue);
};
const removeLocalStorageItem = (key) => {
window.localStorage.removeItem(key);
dispatchStorageEvent(key, null);
};
const localStorageSubscribe = (cb) => {
window.addEventListener("storage", cb);
return () => window.removeEventListener("storage", cb);
};
export const useLocalStorage = (key, initialValue) => {
const getSnapshot = () => getLocalStorageItem(key);
const store = useSyncExternalStore(localStorageSubscribe, getSnapshot);
const setState = useCallback(
(v) => {
try {
let nextState;
if (isFunction(v)) {
const parsedStore = store ? JSON.parse(store) : null;
nextState = v(parsedStore ?? initialValue);
} else {
nextState = v;
}
if (nextState === undefined || nextState === null) {
removeLocalStorageItem(key);
} else {
setLocalStorageItem(key, nextState);
}
} catch (e) {
console.log(e);
}
},
[key, store, initialValue]
);
useEffect(() => {
if (
getLocalStorageItem(key) === null &&
typeof initialValue !== "undefined"
) {
setLocalStorageItem(key, initialValue);
}
}, [key, initialValue]);
return {
current: store ? JSON.parse(store) : initialValue,
setItemValue: setState,
removeItem: () => removeLocalStorageItem(key),
};
};
Requirements
React 18.0 or higher
Parameters
key required
Type: string
The key used to get to the local storage value.
initialValue required
Type: any
The initial value to use if there is no item in local storage with the given key.
Return values
current
Type: any
The current value of the item in the local storage.
setItemValue
Type: function
Set the item value to local storage.
removeItem
Type: function
Remove the item from local storage.
Example
tsx
import { useLocalStorage } from "./hooks/useLocalStorage";
const App = () => {
const { current, setItemValue, removeItem } = useLocalStorage<number>(
"number",
0
);
return (
<div>
<p>Current value: {current}</p>
<button onClick={() => setItemValue(Math.floor(Math.random() * 11))}>
Generate new number
</button>
<button onClick={removeItem}>Delete "number" item</button>
</div>
);
};
export default App;
Use cases
Here are some use cases where this React hook is useful:
- Storing and retrieving user preferences (e.g., theme selection, language choice) in a web application
- Implementing persistent state management for user-generated content (e.g., notes, bookmarks) in a browser-based productivity tool
- Saving and restoring user settings (e.g., font size, color scheme) across sessions in a web-based reading application
- Enabling data persistence for form input values in a multi-step form or form builder tool
- Creating a customizable user experience by allowing users to save and recall their personalized configurations in a web app