atomWithStorage
Ref: https://github.com/pmndrs/jotai/pull/394
import { useAtom } from 'jotai'import { atomWithStorage } from 'jotai/utils'const darkModeAtom = atomWithStorage('darkMode', false)const Page = () => {const [darkMode, setDarkMode] = useAtom(darkModeAtom)return (<><h1>Welcome to {darkMode ? 'dark' : 'light'} mode!</h1><button onClick={() => setDarkMode(!darkMode)}>toggle theme</button></>)}
The atomWithStorage
function creates an atom with a value persisted in localStorage
or sessionStorage
for React or AsyncStorage
for React Native.
Parameters
key (required): a unique string used as the key when syncing state with localStorage, sessionStorage, or AsyncStorage
initialValue (required): the initial value of the atom
storage (optional): an object with getItem
and setItem
methods for storing/retreiving persisted state; defaults to using localStorage
for storage/retreival and JSON.stringify()
/JSON.parse()
for serialization/deserialization.
Optionally, the storage has a subscribe
property, which can be used to synchronize storage. The default localStorage
handles storage
events for cross-tab synchronization.
Server-side rendering
Any JSX markup that depends on the value of a stored atom (e.g., a className
or style
prop) will use the initialValue
when rendered on the server (since localStorage
and sessionStorage
are not available on the server).
This means that there will be a mismatch between what is originally served to the user's browser as HTML and what is expected by React during the rehydration process if the user has a storedValue
that differs from the initialValue
.
The suggested workaround for this issue is to only render the content dependent on the storedValue
client-side by wrapping it in a custom <ClientOnly>
wrapper, which only renders after rehydration. Alternative solutions are technically possible, but would require a brief "flicker" as the initialValue
is swapped to the storedValue
, which can result in an unpleasant user experience, so this solution is advised.
Deleting an item from storage
For the case you want to delete an item from storage,
the atom created with atomWithStorage
accepts the RESET
symbol on write.
See the following example for the usage:
import { useAtom } from 'jotai'import { atomWithStorage, RESET } from 'jotai/utils'const textAtom = atomWithStorage('text', 'hello')const TextBox = () => {const [text, setText] = useAtom(textAtom)return (<><input value={text} onChange={(e) => setText(e.target.value)} /><button onClick={() => setText(RESET)}>Reset (to 'hello')</button></>)}