Skip to main content
Module

x/jotai/docs/utils/atom-with-storage.mdx

👻 Primitive and flexible state management for React
Go to Latest
File
---title: atomWithStorage---
Ref: https://github.com/pmndrs/jotai/pull/394
```jsximport { 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/retrieving persisted state; defaults to using `localStorage` for storage/retrieval 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.- Optionally, the storage has a `delayInit` (`boolean`) property, which can be used to tell jotai whether to use Suspense - `delayInit: true` will not suspend when first reading the value of your atom - `delayInit: false` (default) will suspend when first reading the value of your atom<CodeSandbox id="vuwi7" />## 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](https://www.joshwcomeau.com/react/the-perils-of-rehydration/#abstractions), 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:
```jsximport { 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> </> )}```
If needed, you can also do conditional resets based on previous value.
This can be particularly useful if you wish to clear keys in localStorage if previous values meet a condition.
Below exemplifies this usage that clears the `visible` key whenever the previous value is `true`.
```jsximport { useAtom } from 'jotai'import { atomWithStorage, RESET } from 'jotai/utils'
const isVisibleAtom = atomWithStorage('visible', false)
const TextBox = () => { const [isVisible, setIsVisible] = useAtom(isVisibleAtom) return ( <> { isVisible && <h1>Header is visible!</h1> } <button onClick={() => setIsVisible((prev) => prev ? RESET : true))}>Toggle visible</button> </> )}```
## React-Native implementation
You can use any library that implements `getItem`, `setItem` & `removeItem`.Let's say you would use the standard AsyncStorage provided by the community.
```jsimport { atomWithStorage, createJSONStorage } from 'jotai/utils'import AsyncStorage from '@react-native-async-storage/async-storage'
const storage = createJSONStorage(() => AsyncStorage)const content = {} // anything JSON serializableconst storedAtom = atomWithStorage('stored-key', content, storage)```