useLocalStorage
Like useState
but persisted on localStorage
.
SSG/SSR-friendly.
Enhanced with window storage event.
APIâ
const [state, setState] = useLocalStorage({
key,
initialValue,
fallbackValue,
storageToStateFn,
});
Optionsâ
key: string
(Required)- The localStorage key
initialValue?: any (T)
- Defaults to
null
- The initial value of the state
- Defaults to
fallbackValue?: any (T)
- Defaults to
null
- Will be used if there is no data stored in the localStorage (with the specified key)
- Defaults to
storageToStateFn?: (state: T, { initialValue, fallbackValue }) => any
- Function to re-map the data from the localStorage
- You can add validation here, for example:
(data, { initialValue }) => isExpired ? initialValue : data
Returnsâ
state: T (generic)
- Just like
useState
- Just like
setState: SetStateAction
- Just like
useState
- Just like
Examplesâ
Simpleâ
http://localhost:3000/
Count: 0
âšī¸ Try increment on another browser tab, then back to this tab again
- JS
- TS
import { useLocalStorage } from 'react-power-ups';
export function DemoSimple() {
const [counter, setCounter] = useLocalStorage({
key: 'my-counter',
initialValue: 0,
fallbackValue: 0,
});
return (
<>
<div>Count: {counter}</div>
<button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>
<button onClick={() => window.location.reload()}>Reload window</button>
<button onClick={() => localStorage.clear()}>Remove data in localStorage</button>
</>
);
}
import { useLocalStorage } from 'react-power-ups';
export function DemoSimple() {
const [counter, setCounter] = useLocalStorage<number>({
key: 'my-counter',
initialValue: 0,
fallbackValue: 0,
});
return (
<>
<div>Count: {counter}</div>
<button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>
<button onClick={() => window.location.reload()}>Reload window</button>
<button onClick={() => localStorage.clear()}>Remove data in localStorage</button>
</>
);
}
Advanced (with validation)â
http://localhost:3000/
Count: 1
Will expires in 30s
- JS
- TS
import { useLocalStorage } from 'react-power-ups';
export function DemoValidation() {
const [data, setData] = useLocalStorage({
key: 'my-counter-with-expiration',
initialValue: { value: 1, expiredAt: Date.now() + 30 * 1000 },
fallbackValue: { value: 1, expiredAt: Date.now() + 30 * 1000 },
storageToStateFn: (data, { fallbackValue }) => {
if (data.expiredAt > Date.now()) return data;
return fallbackValue;
},
});
return (
<>
<div>Count: {data.value}</div>
<div>Will expires in {Math.ceil((data.expiredAt - Date.now()) / 1000)}s</div>
<button onClick={() => setData((prev) => ({ ...prev, value: prev.value + 1 }))}>
Increment
</button>
<button onClick={() => window.location.reload()}>Reload window</button>
<button onClick={() => localStorage.setItem('my-counter-with-expiration', '123')}>
Set localStorage data to an invalid value
</button>
<button onClick={() => localStorage.clear()}>Clear localStorage</button>
</>
);
}
import { useLocalStorage } from 'react-power-ups';
type StorageData = {
value: number;
expiredAt: number;
};
export function DemoValidation() {
const [data, setData] = useLocalStorage<StorageData>({
key: 'my-counter-with-expiration',
initialValue: { value: 1, expiredAt: Date.now() + 30 * 1000 },
fallbackValue: { value: 1, expiredAt: Date.now() + 30 * 1000 },
storageToStateFn: (data, { fallbackValue }) => {
if (data.expiredAt > Date.now()) return data;
return fallbackValue;
},
});
return (
<>
<div>Count: {data.value}</div>
<div>Will expires in {Math.ceil((data.expiredAt - Date.now()) / 1000)}s</div>
<button onClick={() => setData((prev) => ({ ...prev, value: prev.value + 1 }))}>
Increment
</button>
<button onClick={() => window.location.reload()}>Reload window</button>
<button onClick={() => localStorage.setItem('my-counter-with-expiration', '123')}>
Set localStorage data to an invalid value
</button>
<button onClick={() => localStorage.clear()}>Clear localStorage</button>
</>
);
}
Code: https://github.com/afiiif/react-power-ups/blob/main/src/use-local-storage.ts