Skip to main content

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
  • fallbackValue?: any (T)
    • Defaults to null
    • Will be used if there is no data stored in the localStorage (with the specified key)
  • 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
  • setState: SetStateAction
    • Just like useState

Examples​

Simple​

http://localhost:3000/
Count: 0
ℹī¸ Try increment on another browser tab, then back to this tab again
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
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