W↓
All docs
🔑
Sign Up/Sign In
jotai.org/docs/
Public Link
Apr 22, 2025, 6:46:06 AM - complete - 113 kB
Apr 22, 2025, 6:46:06 AM - complete - 113 kB
Apr 22, 2025, 6:42:10 AM - complete - 228 kB
Starting URLs:
https://jotai.org/docs/core/atom
Crawl Prefixes:
https://jotai.org/docs/
Exclude Patterns:
https://jotai.org/docs/tools/
https://jotai.org/docs/guides/
https://jotai.org/docs/third-party/
https://jotai.org/docs/recipes/
## Page: https://jotai.org/docs/core/atom ## atom The `atom` function is to create an atom config. We call it "atom config" as it's just a definition and it doesn't yet hold a value. We may also call it just "atom" if the context is clear. An atom config is an immutable object. The atom config object doesn't hold a value. The atom value exists in a store. To create a primitive atom (config), all you need is to provide an initial value. import { atom } from 'jotai' const priceAtom \= atom(10) const messageAtom \= atom('hello') const productAtom \= atom({ id: 12, name: 'good stuff' }) You can also create derived atoms. We have three patterns: * Read-only atom * Write-only atom * Read-Write atom To create derived atoms, we pass a read function and an optional write function. const readOnlyAtom \= atom((get) \=> get(priceAtom) \* 2) const writeOnlyAtom \= atom( null, (get, set, update) \=> { set(priceAtom, get(priceAtom) \- update.discount) set(priceAtom, (price) \=> price \- update.discount) }, ) const readWriteAtom \= atom( (get) \=> get(priceAtom) \* 2, (get, set, newPrice) \=> { set(priceAtom, newPrice / 2) }, ) `get` in the read function is to read the atom value. It's reactive and read dependencies are tracked. `get` in the write function is also to read atom value, but it's not tracked. Furthermore, it can't read unresolved async values in Jotai v1 API. `set` in the write function is to write atom value. It will invoke the write function of the target atom. ### Note about creating an atom in render function Atom configs can be created anywhere, but referential equality is important. They can be created dynamically too. To create an atom in render function, `useMemo` or `useRef` is required to get a stable reference. If in doubt about using `useMemo` or `useRef` for memoization, use `useMemo`. Otherwise, it can cause infinite loop with `useAtom`. const Component \= ({ value }) \=> { const valueAtom \= useMemo(() \=> atom({ value }), \[value\]) } ### Signatures function atom<Value\>(initialValue: Value): PrimitiveAtom<Value\> // read-only atom function atom<Value\>(read: (get: Getter) => Value): Atom<Value\> // writable derived atom function atom<Value, Args extends unknown\[\], Result>( read: (get: Getter) => Value, write: (get: Getter, set: Setter, ...args: Args) => Result, ): WritableAtom<Value, Args, Result> // write-only derived atom function atom<Value, Args extends unknown\[\], Result>( read: Value, write: (get: Getter, set: Setter, ...args: Args) => Result, ): WritableAtom<Value, Args, Result> * `initialValue`: the initial value that the atom will return until its value is changed. * `read`: a function that's evaluated whenever the atom is read. The signature of `read` is `(get) => Value`, and `get` is a function that takes an atom config and returns its value stored in Provider as described below. Dependency is tracked, so if `get` is used for an atom at least once, the `read` will be reevaluated whenever the atom value is changed. * `write`: a function mostly used for mutating atom's values, for a better description; it gets called whenever we call the second value of the returned pair of `useAtom`, the `useAtom()[1]`. The default value of this function in the primitive atom will change the value of that atom. The signature of `write` is `(get, set, ...args) => Result`. `get` is similar to the one described above, but it doesn't track the dependency. `set` is a function that takes an atom config and a new value which then updates the atom value in Provider. `...args` is the arguments that we receive when we call `useAtom()[1]`. `Result` is the return value of the `write` function. const primitiveAtom \= atom(initialValue) const derivedAtomWithRead \= atom(read) const derivedAtomWithReadWrite \= atom(read, write) const derivedAtomWithWriteOnly \= atom(null, write) There are two kinds of atoms: a writable atom and a read-only atom. Primitive atoms are always writable. Derived atoms are writable if the `write` is specified. The `write` of primitive atoms is equivalent to the `setState` of `React.useState`. ### `debugLabel` property The created atom config can have an optional property `debugLabel`. The debug label is used to display the atom in debugging. See Debugging guide for more information. Note: While, the debug labels don’t have to be unique, it’s generally recommended to make them distinguishable. ### `onMount` property The created atom config can have an optional property `onMount`. `onMount` is a function which takes a function `setAtom` and returns `onUnmount` function optionally. The `onMount` function is called when the atom is subscribed in the provider in the first time, and `onUnmount` is called when it’s no longer subscribed. In some cases (like React strict mode), an atom can be unmounted and then mounted immediately. const anAtom \= atom(1) anAtom.onMount \= (setAtom) \=> { console.log('atom is mounted in provider') setAtom(c \=> c + 1) return () \=> { ... } } const Component \= () \=> { useAtom(anAtom) useAtomValue(anAtom) useSetAtom(anAtom) useAtomCallback( useCallback((get) \=> get(anAtom), \[\]), ) } Calling `setAtom` function will invoke the atom’s `write`. Customizing `write` allows changing the behavior. const countAtom \= atom(1) const derivedAtom \= atom( (get) \=> get(countAtom), (get, set, action) \=> { if (action.type \=== 'init') { set(countAtom, 10) } else if (action.type \=== 'inc') { set(countAtom, (c) \=> c + 1) } }, ) derivedAtom.onMount \= (setAtom) \=> { setAtom({ type: 'init' }) } ### Advanced API Since Jotai v2, the `read` function has the second argument `options`. #### `options.signal` It uses AbortController so that you can abort async functions. Abort is triggered before new calculation (invoking `read` function) is started. How to use it: const readOnlyDerivedAtom \= atom(async (get, { signal }) \=> { }) const writableDerivedAtom \= atom( async (get, { signal }) \=> { }, (get, set, arg) \=> { }, ) The `signal` value is AbortSignal. You can check `signal.aborted` boolean value, or use `abort` event with `addEventListener`. For `fetch` use case, we can simply pass `signal`. See the below example for `fetch` usage. #### `options.setSelf` It's a special function to invoke the write function of the self atom. ⚠️ It's provided primarily for internal usage and third-party library authors. Read the source code carefully to understand the behavior. Check release notes for any breaking/non-breaking changes. ## Stackblitz import { Suspense } from 'react' import { atom, useAtom } from 'jotai' const userIdAtom \= atom(1) const userAtom \= atom(async (get, { signal }) \=> { const userId \= get(userIdAtom) const response \= await fetch( \`https://jsonplaceholder.typicode.com/users/${userId}?\_delay=2000\`, { signal }, ) return response.json() }) const Controls \= () \=> { const \[userId, setUserId\] \= useAtom(userIdAtom) return ( <div\> User Id: {userId} <button onClick\={() \=> setUserId((c) \=> c \- 1)}\>Prev</button\> <button onClick\={() \=> setUserId((c) \=> c + 1)}\>Next</button\> </div\> ) } const UserName \= () \=> { const \[user\] \= useAtom(userAtom) return <div\>User name: {user.name}</div\> } const App \= () \=> ( <\> <Controls /> <Suspense fallback\="Loading..."\> <UserName /> </Suspense\> </\> ) export default App --- ## Page: https://jotai.org/docs/core/use-atom ## useAtom The `useAtom` hook is used to read an atom from the state. The state can be seen as a WeakMap of atom configs and atom values. The `useAtom` hook returns the atom value and an update function as a tuple, just like React's `useState`. It takes an atom config created with `atom()` as a parameter. At the creation of the atom config, there is no value associated with it. Only once the atom is used via `useAtom`, does the initial value get stored in the state. If the atom is a derived atom, the read function is called to compute its initial value. When an atom is no longer used, meaning all the components using it are unmounted and the atom config no longer exists, the value in the state is garbage collected. const \[value, setValue\] \= useAtom(anAtom) The `setValue` takes just one argument, which will be passed to the write function of the atom as the third parameter. The end result depends on how the write function is implemented. If the write function is not explicitly set, the atom will simply receive the value passed as a parameter to `setValue`. **Note:** as mentioned in the _atom_ section, referential equality is important when creating atoms, so you need to handle it properly otherwise it can cause infinite loops. const stableAtom \= atom(0) const Component \= () \=> { const \[atomValue\] \= useAtom(atom(0)) const \[atomValue\] \= useAtom(stableAtom) const \[derivedAtomValue\] \= useAtom( useMemo( () \=> atom((get) \=> get(stableAtom) \* 2), \[\], ), ) } **Note**: Remember that React is responsible for calling your component, meaning it has to be idempotent, ready to be called multiple times. You will often see an extra re-render even if no props or atoms have changed. An extra re-render without a commit is an expected behavior, since it is the default behavior of useReducer in React 18. ### Signatures function useAtom<Value, Update\>( atom: WritableAtom<Value, Update\>, options?: { store?: Store }, ): \[Value, SetAtom<Update\>\] // read-only atom function useAtom<Value\>( atom: Atom<Value\>, options?: { store?: Store }, ): \[Value, never\] ### How atom dependency works Every time we invoke the "read" function, we refresh the dependencies and dependents. > The read function is the first parameter of the atom. If B depends on A, it means that A is a dependency of B, and B is a dependent on A. const uppercaseAtom \= atom((get) \=> get(textAtom).toUpperCase()) When you create the atom, the dependency will not be present. On first use, we run the read function and conclude that `uppercaseAtom` depends on `textAtom`. So `uppercaseAtom` is added to the dependents of `textAtom`. When we re-run the read function of `uppercaseAtom` (because its `textAtom` dependency is updated), the dependency is created again, which is the same in this case. We then remove stale dependents from `textAtom` and replace them with their latest versions. ### Atoms can be created on demand While the basic examples here show defining atoms globally outside components, there's no restrictions about where or when we can create an atom. As long as we remember that atoms are identified by their object referential identity, we can create them anytime. If you create atoms in render functions, you would typically want to use a hook like `useRef` or `useMemo` for memoization. If not, the atom would be re-created each time the component renders. You can create an atom and store it with `useState` or even in another atom. See an example in issue #5. You can also cache atoms somewhere globally. See this example or that example. Check `atomFamily` in utils for parameterized atoms. ## useAtomValue const countAtom \= atom(0) const Counter \= () \=> { const setCount \= useSetAtom(countAtom) const count \= useAtomValue(countAtom) return ( <\> <div\>count: {count}</div\> <button onClick\={() \=> setCount(count + 1)}\>+1</button\> </\> ) } Similar to the `useSetAtom` hook, `useAtomValue` allows you to access a read-only atom. Nonetheless, it can also be used to access read-write atom's values. ## useSetAtom const switchAtom \= atom(false) const SetTrueButton \= () \=> { const setCount \= useSetAtom(switchAtom) const setTrue \= () \=> setCount(true) return ( <div\> <button onClick\={setTrue}\>Set True</button\> </div\> ) } const SetFalseButton \= () \=> { const setCount \= useSetAtom(switchAtom) const setFalse \= () \=> setCount(false) return ( <div\> <button onClick\={setFalse}\>Set False</button\> </div\> ) } export default function App() { const state \= useAtomValue(switchAtom) return ( <div\> State: <b\>{state.toString()}</b\> <SetTrueButton /> <SetFalseButton /> </div\> ) } In case you need to update a value of an atom without reading it, you can use `useSetAtom()`. This is especially useful when the performance is a concern, as the `const [, setValue] = useAtom(valueAtom)` will cause unnecessary rerenders on each `valueAtom` update. --- ## Page: https://jotai.org/docs/core/store ## createStore This function is to create a new empty store. The store can be used to pass in `Provider`. The store has three methods: `get` for getting atom values, `set` for setting atom values, and `sub` for subscribing to atom changes. const myStore \= createStore() const countAtom \= atom(0) myStore.set(countAtom, 1) const unsub \= myStore.sub(countAtom, () \=> { console.log('countAtom value is changed to', myStore.get(countAtom)) }) const Root \= () \=> ( <Provider store\={myStore}\> <App /> </Provider\> ) ## getDefaultStore This function returns a default store that is used in provider-less mode. const defaultStore \= getDefaultStore() --- ## Page: https://jotai.org/docs/core/provider ## Provider The `Provider` component is to provide state for a component sub tree. Multiple Providers can be used for multiple subtrees, and they can even be nested. This works just like React Context. If an atom is used in a tree without a Provider, it will use the default state. This is so-called provider-less mode. Providers are useful for three reasons: 1. To provide a different state for each sub tree. 2. To accept initial values of atoms. 3. To clear all atoms by remounting. const SubTree \= () \=> ( <Provider\> <Child /> </Provider\> ) ### Signatures const Provider: React.FC<{ store?: Store }\> Atom configs don't hold values. Atom values reside in separate stores. A Provider is a component that contains a store and provides atom values under the component tree. A Provider works like React context provider. If you don't use a Provider, it works as provider-less mode with a default store. A Provider will be necessary if we need to hold different atom values for different component trees. Provider can take an optional prop `store`. const Root \= () \=> ( <Provider\> <App /> </Provider\> ) ### `store` prop A Provider accepts an optional prop `store` that you can use for the Provider subtree. #### Example const myStore \= createStore() const Root \= () \=> ( <Provider store\={myStore}\> <App /> </Provider\> ) ## useStore This hook returns a store within the component tree. const Component \= () \=> { const store \= useStore() } --- ## Page: https://jotai.org/docs/utilities/storage ## 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 the following methods: * **getItem(key, initialValue)** (required): Reads an item from storage, or falls back to the `intialValue` * **setItem(key, value)** (required): Saves an item to storage * **removeItem(key)** (required): Deletes the item from storage * **subscribe(key, callback, initialValue)** (optional): A method which subscribes to external storage updates. **options** (optional): an object with the following properties: * **getOnInit** (optional, by default **false**): A boolean value indicating whether to get item from storage on initialization. Note that in an SPA with `getOnInit` either not set or `false` you will always get the initial value instead of the stored value on initialization. If the stored value is preferred set `getOnInit` to `true`. If not specified, the default storage implementation uses `localStorage` for storage/retrieval, `JSON.stringify()`/`JSON.parse()` for serialization/deserialization, and subscribes to `storage` events for cross-tab synchronization. ### `createJSONStorage` util To create a custom storage implementation with `JSON.stringify()`/`JSON.parse()` for the `storage` option, `createJSONStorage` util is provided. Usage: const storage \= createJSONStorage( () \=> localStorage, { reviver, replacer, }, ) Note: `JSON.parse` is not type safe. If it can't accept any types, some kind of validation would be necessary for production apps. ### 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\> </\> ) } 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`. import { 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. import { atomWithStorage, createJSONStorage } from 'jotai/utils' import AsyncStorage from '@react-native-async-storage/async-storage' const storage \= createJSONStorage(() \=> AsyncStorage) const content \= {} const storedAtom \= atomWithStorage('stored-key', content, storage) #### Notes with AsyncStorage (since v2.2.0) With AsyncStorage (as with any asynchronous storage), the atom value becomes async. When updating the atom by referencing the current value, then you'll need to `await` it. const countAtom \= atomWithStorage('count-key', 0, anyAsyncStorage) const Component \= () \=> { const \[count, setCount\] \= useAtom(countAtom) const increment \= () \=> { setCount(async (promiseOrValue) \=> (await promiseOrValue) + 1) } } ### Validating stored values To add runtime validation to your storage atoms, you will need to create a custom implementation of storage. Below is an example that utilizes Zod to validate values stored in `localStorage` with cross-tab synchronization. import { atomWithStorage } from 'jotai/utils' import { z } from 'zod' const myNumberSchema \= z.number().int().nonnegative() const storedNumberAtom \= atomWithStorage('my-number', 0, { getItem(key, initialValue) { const storedValue \= localStorage.getItem(key) try { return myNumberSchema.parse(JSON.parse(storedValue ?? '')) } catch { return initialValue } }, setItem(key, value) { localStorage.setItem(key, JSON.stringify(value)) }, removeItem(key) { localStorage.removeItem(key) }, subscribe(key, callback, initialValue) { if ( typeof window \=== 'undefined' || typeof window.addEventListener \=== 'undefined' ) { return } window.addEventListener('storage', (e) \=> { if (e.storageArea \=== localStorage && e.key \=== key) { let newValue try { newValue \= myNumberSchema.parse(JSON.parse(e.newValue ?? '')) } catch { newValue \= initialValue } callback(newValue) } }) }, }) We also have a new util `unstable_withStorageValidator` to simplify some cases. The above case would become: import { atomWithStorage, createJSONStorage, unstable\_withStorageValidator as withStorageValidator, } from 'jotai/utils' import { z } from 'zod' const myNumberSchema \= z.number().int().nonnegative() const isMyNumber \= (v) \=> myNumberSchema.safeParse(v).success const storedNumberAtom \= atomWithStorage( 'my-number', 0, withStorageValidator(isMyNumber)(createJSONStorage()), ) --- ## Page: https://jotai.org/docs/utilities/ssr ## useHydrateAtoms Ref: https://github.com/pmndrs/jotai/issues/340 ### Usage import { atom, useAtom } from 'jotai' import { useHydrateAtoms } from 'jotai/utils' const countAtom \= atom(0) const CounterPage \= ({ countFromServer }) \=> { useHydrateAtoms(\[\[countAtom, countFromServer\]\]) const \[count\] \= useAtom(countAtom) } The primary use case for `useHydrateAtoms` are SSR apps like Next.js, where an initial value is e.g. fetched on the server, which can be passed to a component by props. ⚠️ Note: Although the term "hydrate" might suggest server-side usage, this hook is designed for client-side code and should be used with the `'use client'` directive. function useHydrateAtoms( values: Iterable<readonly \[Atom<unknown\>, unknown\]>, options?: { store?: Store }, ): void The hook takes an iterable of tuples containing `[atom, value]` as an argument and optional options. useHydrateAtoms( \[ \[countAtom, 42\], \[frameworkAtom, 'Next.js'\], \], { store: myStore }, ) useHydrateAtoms(new Map(\[\[count, 42\]\])) Atoms can only be hydrated once per store. Therefore, if the initial value used is changed during rerenders, it won't update the atom value. If there is a unique need to re-hydrate a previously hydrated atom, pass the optional dangerouslyForceHydrate as true and note that it may behave wrongly in concurrent rendering. useHydrateAtoms( \[ \[countAtom, 42\], \[frameworkAtom, 'Next.js'\], \], { dangerouslyForceHydrate: true, }, ) If there's a need to hydrate in multiple stores, use multiple `useHydrateAtoms` hooks to achieve that. useHydrateAtoms(\[ \[countAtom, 42\], \[frameworkAtom, 'Next.js'\], \]) useHydrateAtoms( \[ \[countAtom, 17\], \[frameworkAtom, 'Gatsby'\], \], { store: myStore }, ) If you are using TypeScript with target `ES5`, you might need `as const` cast on the array to preserve the tuple type. useHydrateAtoms(\[ \[countAtom, 42\], \[frameworkAtom, 'Next.js'\], \] as const) Or you may need to use a Map when passing the atom value to useHydrateAtoms. You can find a working example in the Initializing State on Render docs. ### Demo There's more examples in the Next.js section. --- ## Page: https://jotai.org/docs/utilities/async All atoms support async behavior such as async read or async write. However there are APIs for more control described here. ## loadable If you don't want async atoms to suspend or throw to an error boundary (for example, for finer-grained control of loading and error logic), you can use the `loadable` util. It would work the same way for any atom. Simply wrap your atoms with the `loadable` util. It returns a value with one of three states: `loading`, `hasData` and `hasError`. { state: 'loading' | 'hasData' | 'hasError', data?: any, error?: any, } import { loadable } from "jotai/utils" const asyncAtom \= atom(async (get) \=> ...) const loadableAtom \= loadable(asyncAtom) const Component \= () \=> { const \[value\] \= useAtom(loadableAtom) if (value.state \=== 'hasError') return <Text\>{value.error}</Text\> if (value.state \=== 'loading') { return <Text\>Loading...</Text\> } console.log(value.data) return <Text\>Value: {value.data}</Text\> } ## atomWithObservable Ref: https://github.com/pmndrs/jotai/pull/341 ### Usage import { useAtom } from 'jotai' import { atomWithObservable } from 'jotai/utils' import { interval } from 'rxjs' import { map } from 'rxjs/operators' const counterSubject \= interval(1000).pipe(map((i) \=> \`#${i}\`)) const counterAtom \= atomWithObservable(() \=> counterSubject) const Counter \= () \=> { const \[counter\] \= useAtom(counterAtom) return <div\>count: {counter}</div\> } The `atomWithObservable` function creates an atom from a rxjs (or similar) `subject` or `observable`. Its value will be last value emitted from the stream. To use this atom, you need to wrap your component with `<Suspense>`. Check out guides/async. ### Initial value `atomWithObservable` takes second optional parameter `{ initialValue }` that allows to specify initial value for the atom. If `initialValue` is provided then `atomWithObservable` will not suspend and will show initial value before receiving first value from observable. `initialValue` can be either a value or a function that returns a value const counterAtom \= atomWithObservable(() \=> counterSubject, { initialValue: 10, }) const counterAtom2 \= atomWithObservable(() \=> counterSubject, { initialValue: () \=> Math.random(), }) ### Stackblitz ## unwrap The `unwrap` util will convert an async atom to a sync atom like `loadable`. Unlike `loadable`, the fallback value can be configured. Unlike `loadable`, the error won't be handled and just thrown. The use case of `unwrap` is to ease deriving atoms. This is especially useful for v2 API, because `get` in the read function doesn't resolve promises. ### Signature function unwrap<Value, Args extends unknown\[\], Result\>( anAtom: WritableAtom<Value, Args, Result\>, ): WritableAtom<Awaited<Value\> | undefined, Args, Result> function unwrap<Value, Args extends unknown\[\], Result, PendingValue>( anAtom: WritableAtom<Value, Args, Result>, fallback: (prev?: Awaited<Value\>) => PendingValue, ): WritableAtom<Awaited<Value\> | PendingValue, Args, Result> function unwrap<Value\>(anAtom: Atom<Value\>): Atom<Awaited<Value\> | undefined> function unwrap<Value, PendingValue>( anAtom: Atom<Value\>, fallback: (prev?: Awaited<Value\>) => PendingValue, ): Atom<Awaited<Value\> | PendingValue> ### Usage import { atom } from 'jotai' import { unwrap } from 'jotai/utils' const countAtom \= atom(0) const delayedCountAtom \= atom(async (get) \=> { await new Promise((r) \=> setTimeout(r, 500)) return get(countAtom) }) const unwrapped1Atom \= unwrap(delayedCountAtom) const unwrapped2Atom \= unwrap(delayedCountAtom, (prev) \=> prev ?? 0) --- ## Page: https://jotai.org/docs/utilities/lazy When defining primitive atoms, their initial value has to be bound at definition time. If creating that initial value is computationally expensive, or the value is not accessible during definition, it would be best to postpone the atom's initialization until its first use in the store. const imageDataAtom \= atom(initializeExpensiveImage()) function Home() { ... } function ImageEditor() { const \[imageData, setImageData\] \= useAtom(imageDataAtom); ... } function App() { return ( <Router\> <Route path\="/" component\={Home} /> <Route path\="/edit" component\={ImageEditor} /> </Router\> ) } ## atomWithLazy Ref: https://github.com/pmndrs/jotai/pull/2465 We can use `atomWithLazy` to create a primitive atom whose initial value will be computed at first use in the store. After initialization, it will behave like a regular primitive atom (can be written to). ### Usage import { atomWithLazy } from 'jotai/utils' const imageDataAtom \= atomWithLazy(initializeExpensiveImage) function Home() { ... } function ImageEditor() { const \[imageData, setImageData\] \= useAtom(imageDataAtom); ... } function App() { return ( <Router\> <Route path\="/" component\={Home} /> <Route path\="/edit" component\={ImageEditor} /> </Router\> ) } ### Using multiple stores Since each store is its separate universe, the initial value will be recreated exactly once per store (unless using something like `jotai-scope`, which fractures a store into smaller universes). type RGB \= \[number, number, number\]; function randomRGB(): RGB { ... } const lift \= (value: number) \=> (\[r, g, b\]: RGB) \=> { return \[r + value, g + value, b + value\] } const colorAtom \= lazyAtom(randomRGB) let store \= createStore() console.log(store.get(colorAtom)) store.set(colorAtom, lift(8)) console.log(store.get(colorAtom)) store \= createStore() console.log(store.get(colorAtom)) --- ## Page: https://jotai.org/docs/utilities/resettable ## atomWithReset Ref: https://github.com/pmndrs/jotai/issues/41 function atomWithReset<Value\>( initialValue: Value, ): WritableAtom<Value, SetStateAction<Value\> | typeof RESET> Creates an atom that could be reset to its `initialValue` with `useResetAtom` hook. It works exactly the same way as primitive atom would, but you are also able to set it to a special value `RESET`. See examples in Resettable atoms. ### Example import { atomWithReset } from 'jotai/utils' const dollarsAtom \= atomWithReset(0) const todoListAtom \= atomWithReset(\[ { description: 'Add a todo', checked: false }, \]) ## RESET Ref: https://github.com/pmndrs/jotai/issues/217 const RESET: unique symbol Special value that is accepted by Resettable atoms created with `atomWithReset`, `atomWithDefault` or writable atom created with `atom` if it accepts `RESET` symbol. ### Example import { atom, useSetAtom } from 'jotai' import { atomWithReset, useResetAtom, RESET } from 'jotai/utils' const dollarsAtom \= atomWithReset(0) const centsAtom \= atom( (get) \=> get(dollarsAtom) \* 100, (get, set, newValue: number | typeof RESET) \=> set(dollarsAtom, newValue \=== RESET ? newValue : newValue / 100) ) const ResetExample \= () \=> { const setDollars \= useSetAtom(dollarsAtom) const resetCents \= useResetAtom(centsAtom) return ( <\> <button onClick\={() \=> setDollars(RESET)}\>Reset dollars</button\> <button onClick\={resetCents}\>Reset cents</button\> </\> ) } ## useResetAtom function useResetAtom<Value\>( anAtom: WritableAtom<Value, typeof RESET>, ): () => void | Promise<void\> Resets a Resettable atom to its initial value. ### Example import { useResetAtom } from 'jotai/utils' import { todoListAtom } from './store' const TodoResetButton \= () \=> { const resetTodoList \= useResetAtom(todoListAtom) return <button onClick\={resetTodoList}\>Reset</button\> } ## atomWithDefault Ref: https://github.com/pmndrs/jotai/issues/352 ### Usage This is a function to create a resettable primitive atom. Its default value can be specified with a read function instead of a static initial value. import { atomWithDefault } from 'jotai/utils' const count1Atom \= atom(1) const count2Atom \= atomWithDefault((get) \=> get(count1Atom) \* 2) ### Stackblitz ### Resetting default values You can reset the value of an `atomWithDefault` atom to its original default value. import { useAtom } from 'jotai' import { atomWithDefault, useResetAtom, RESET } from 'jotai/utils' const count1Atom \= atom(1) const count2Atom \= atomWithDefault((get) \=> get(count1Atom) \* 2) const Counter \= () \=> { const \[count1, setCount1\] \= useAtom(count1Atom) const \[count2, setCount2\] \= useAtom(count2Atom) const resetCount2 \= useResetAtom(count2Atom) return ( <\> <div\> count1: {count1}, count2: {count2} </div\> <button onClick\={() \=> setCount1((c) \=> c + 1)}\>increment count1</button\> <button onClick\={() \=> setCount2((c) \=> c + 1)}\>increment count2</button\> <button onClick\={() \=> resetCount2()}\>Reset with useResetAtom</button\> <button onClick\={() \=> setCount2(RESET)}\>Reset with RESET const</button\> </\> ) } This can be useful when an `atomWithDefault` atom value is overwritten using the `set` function, in which case the provided `getter` function is no longer used and any change in dependencies atoms will not trigger an update. Resetting the value allows us to restore its original default value, discarding changes made previously via the `set` function. ## atomWithRefresh function atomWithRefresh<Value\>( read: Read<Value, \[\], void>, ): WritableAtom<Value, \[\], void> Creates an atom that we can refresh, which is to force reevaluating the read function. This is helpful when you need to refresh asynchronous data. It can also be used to implement "pull to refresh" functionality. function atomWithRefresh<Value, Args extends unknown\[\], Result\>( read: Read<Value, Args, Result\>, write: Write<Value, Args, Result\>, ): WritableAtom<Value, Args | \[\], Result | void\> Passing zero arguments to `set` will refresh. Passing one or more arguments to `set` will call "write" function. ### Example Here's how you'd use it to implement an refresh-able source of data: import { atomWithRefresh } from 'jotai/utils' const postsAtom \= atomWithRefresh((get) \=> fetch('https://jsonplaceholder.typicode.com/posts').then((r) \=> r.json()), ) In a component: const PostsList \= () \=> { const \[posts, refreshPosts\] \= useAtom(postsAtom) return ( <div\> <ul\> {posts.map((post) \=> ( <li key\={post.id}\>{post.title}</li\> ))} </ul\> {} <button type\="button" onClick\={() \=> refreshPosts()}\> Refresh posts </button\> </div\> ) } --- ## Page: https://jotai.org/docs/utilities/family ## atomFamily Ref: https://github.com/pmndrs/jotai/issues/23 ### Usage atomFamily(initializeAtom, areEqual): (param) \=> Atom This will create a function that takes `param` and returns an atom. If the atom has already been created, it will be returned from the cache. `initializeAtom` is a function that can return any kind of atom (`atom()`, `atomWithDefault()`, ...). Note that the `areEqual` argument is optional and compares if two params are equal (defaults to `Object.is`). To reproduce behavior similar to Recoil's atomFamily/selectorFamily, specify a deepEqual function to `areEqual`. For example: import { atom } from 'jotai' import { atomFamily } from 'jotai/utils' import deepEqual from 'fast-deep-equal' const fooFamily \= atomFamily((param) \=> atom(param), deepEqual) ### TypeScript The atom family types will be inferred from initializeAtom. Here's a typical usage with a primitive atom. import type { PrimitiveAtom } from 'jotai' const myFamily \= atomFamily((id: number) \=> atom(id)). You can explicitly declare the type of parameter, value, and atom's setState function using TypeScript generics. atomFamily<Param, Value, Update\>(initializeAtom: (param: Param) \=> WritableAtom<Value, Update\>, areEqual?: (a: Param, b: Param) \=> boolean) atomFamily<Param, Value\>(initializeAtom: (param: Param) \=> Atom<Value\>, areEqual?: (a: Param, b: Param) => boolean) If you want to explicitly declare the atomFamily for a primitive atom, you need to use `SetStateAction`. type SetStateAction<Value\> = Value | ((prev: Value) => Value) const myFamily = atomFamily<number, number, SetStateAction<number\>\>( (id: number) => atom(id), ) ### Caveat: Memory Leaks Internally, atomFamily is just a Map whose key is a param and whose value is an atom config. Unless you explicitly remove unused params, this leads to memory leaks. This is crucial if you use infinite number of params. There are two ways to remove params. * `myFamily.remove(param)` allows you to remove a specific param. * `myFamily.setShouldRemove(shouldRemove)` is to register `shouldRemove` function which runs immediately **and** when you are about to get an atom from a cache. * shouldRemove is a function that takes two arguments `createdAt` in milliseconds and `param`, and returns a boolean value. * setting `null` will remove the previously registered function. ### Examples import { atom } from 'jotai' import { atomFamily } from 'jotai/utils' const todoFamily \= atomFamily((name) \=> atom(name)) todoFamily('foo') import { atom } from 'jotai' import { atomFamily } from 'jotai/utils' const todoFamily \= atomFamily((name) \=> atom( (get) \=> get(todosAtom)\[name\], (get, set, arg) \=> { const prev \= get(todosAtom) set(todosAtom, { ...prev, \[name\]: { ...prev\[name\], ...arg } }) }, ), ) import { atom } from 'jotai' import { atomFamily } from 'jotai/utils' const todoFamily \= atomFamily( ({ id, name }) \=> atom({ name }), (a, b) \=> a.id \=== b.id, ) ### Stackblitz --- ## Page: https://jotai.org/docs/utilities/callback ## useAtomCallback Ref: https://github.com/pmndrs/jotai/issues/60 ### Usage useAtomCallback<Result, Args extends unknown\[\]\>( callback: (get: Getter, set: Setter, ...arg: Args) \=> Result, options?: Options ): (...args: Args) \=> Result This hook is for interacting with atoms imperatively. It takes a callback function that works like atom write function, and returns a function that returns an atom value. The callback to pass in the hook must be stable (should be wrapped with useCallback). ### Examples import { useEffect, useState, useCallback } from 'react' import { Provider, atom, useAtom } from 'jotai' import { useAtomCallback } from 'jotai/utils' const countAtom \= atom(0) const Counter \= () \=> { const \[count, setCount\] \= useAtom(countAtom) return ( <\> {count} <button onClick\={() \=> setCount((c) \=> c + 1)}\>+1</button\> </\> ) } const Monitor \= () \=> { const \[count, setCount\] \= useState(0) const readCount \= useAtomCallback( useCallback((get) \=> { const currCount \= get(countAtom) setCount(currCount) return currCount }, \[\]), ) useEffect(() \=> { const timer \= setInterval(async () \=> { console.log(readCount()) }, 1000) return () \=> { clearInterval(timer) } }, \[readCount\]) return <div\>current count: {count}</div\> } ### Stackblitz --- ## Page: https://jotai.org/docs/utilities/reducer ## atomWithReducer Ref: https://github.com/pmndrs/jotai/issues/38 import { atomWithReducer } from 'jotai/utils' const countReducer \= (prev, action) \=> { if (action.type \=== 'inc') return prev + 1 if (action.type \=== 'dec') return prev \- 1 throw new Error('unknown action type') } const countReducerAtom \= atomWithReducer(0, countReducer) ### Stackblitz ## useReducerAtom See useReducerAtom recipe. --- ## Page: https://jotai.org/docs/utilities/select ## selectAtom ⚠️ Unlike its name, `selectAtom` is provided as an escape hatch. Using it means building not 100% pure atom model. Prefer using derived atoms and use `selectAtom` only when `equalityFn` or `prevSlice` is unavoidable. ### Signatures function selectAtom<Value, Slice\>( anAtom: Atom<Value\>, selector: (v: Value, prevSlice?: Slice) => Slice, equalityFn: (a: Slice, b: Slice) => boolean = Object.is, ): Atom<Slice\> This function creates a derived atom whose value is a function of the original atom's value, determined by `selector.` The selector function runs whenever the original atom changes; it updates the derived atom only if `equalityFn` reports that the derived value has changed. By default, `equalityFn` is reference equality, but you can supply your favorite deep-equals function to stabilize the derived value where necessary. ### Examples const defaultPerson \= { name: { first: 'Jane', last: 'Doe', }, birth: { year: 2000, month: 'Jan', day: 1, time: { hour: 1, minute: 1, }, }, } const personAtom \= atom(defaultPerson) const nameAtom \= selectAtom(personAtom, (person) \=> person.name) const birthAtom \= selectAtom(personAtom, (person) \=> person.birth, deepEquals) ### Hold stable references As always, to prevent an infinite loop when using `useAtom` in render cycle, you must provide `useAtom` a stable reference of your atoms. For `selectAtom`, we need **both** the base atom and the selector to be stable. const \[value\] \= useAtom(selectAtom(atom(0), (val) \=> val)) You have multiple options in order to satisfy these constraints: const baseAtom \= atom(0) const baseSelector \= (v) \=> v const Component \= () \=> { const \[value\] \= useAtom(useMemo(() \=> selectAtom(baseAtom, (v) \=> v), \[\])) const \[value\] \= useAtom( selectAtom( baseAtom, useCallback((v) \=> v, \[\]), ), ) const \[value\] \= useAtom(selectAtom(baseAtom, baseSelector)) } ### Stackblitz --- ## Page: https://jotai.org/docs/utilities/split ## splitAtom The `splitAtom` utility is designed for scenarios where you need to obtain an atom for each element in a list. It operates on read/write atoms containing a list, returning an atom that holds a list of atoms, each corresponding to an item in the original list. ### Signature A simplified type signature for `splitAtom` would be: type SplitAtom \= <Item, Key\>( arrayAtom: PrimitiveAtom<Array<Item\>\>, keyExtractor?: (item: Item) => Key ): Atom<Array<PrimitiveAtom<Item\>\>> ### Key Features 1. The returned atom contains a dispatch function in the `write` direction (since v1.6.4), providing a simple way to modify the original atom with actions like `remove`, `insert`, and `move`. 2. An optional `keyExtractor` function can be provided as a second argument to enhance stability and performance. The `splitAtom` utility supports a second argument which is a key extractor function: export function splitAtom<Item, Key\>( arrAtom: WritableAtom<Item\[\], \[Item\[\]\], void\> | Atom<Item\[\]\>, keyExtractor?: (item: Item) \=> Key, ) Important considerations for the `keyExtractor`: * If `splitAtom` is used within a React render loop, the `keyExtractor` must be a stable function that maintains object equality (shallow equality). This requirement doesn't apply outside of render loops. * Providing a `keyExtractor` enhances the stability of the atom output (which makes memoization hit cache more often). It prevents unnecessary subatom recreation due to index shifts in the source array. * `keyExtractor` is an optional optimization that should only be used if the extracted key is guaranteed unique for each item in the array. ### Example Usage Here's an example demonstrating the use of `splitAtom`: import { Provider, atom, useAtom, PrimitiveAtom } from 'jotai' import { splitAtom } from 'jotai/utils' import './styles.css' const initialState \= \[ { task: 'help the town', done: false, }, { task: 'feed the dragon', done: false, }, \] const todosAtom \= atom(initialState) const todoAtomsAtom \= splitAtom(todosAtom) type TodoType \= (typeof initialState)\[number\] const TodoItem \= ({ todoAtom, remove, }: { todoAtom: PrimitiveAtom<TodoType\> remove: () => void }) => { const \[todo, setTodo\] \= useAtom(todoAtom) return ( <div\> <input value\={todo.task} onChange\={(e) \=> { setTodo((oldValue) \=> ({ ...oldValue, task: e.target.value })) }} /> <input type\="checkbox" checked\={todo.done} onChange\={() \=> { setTodo((oldValue) \=> ({ ...oldValue, done: !oldValue.done })) }} /> <button onClick\={remove}\>remove</button\> </div\> ) } const TodoList = () => { const \[todoAtoms, dispatch\] \= useAtom(todoAtomsAtom) return ( <ul\> {todoAtoms.map((todoAtom) \=> ( <TodoItem todoAtom\={todoAtom} remove\={() \=> dispatch({ type: 'remove', atom: todoAtom })} /> ))} </ul\> ) } const App = () => ( <Provider\> <TodoList /> </Provider\> ) export default App This example demonstrates how to use `splitAtom` to manage a list of todo items, allowing individual manipulation of each item while maintaining the overall list atom. --- ## Page: https://jotai.org/docs/extensions/trpc You can use Jotai with tRPC. ### Install You have to install `jotai-trpc`, `@trpc/client` and `@trpc/server` to use the extension. npm install jotai\-trpc @trpc/client @trpc/server ### Usage import { createTRPCJotai } from 'jotai-trpc' const trpc \= createTRPCJotai<MyRouter\>({ links: \[ httpLink({ url: myUrl, }), \], }) const idAtom = atom('foo') const queryAtom = trpc.bar.baz.atomWithQuery((get) => get(idAtom)) ### atomWithQuery `...atomWithQuery` creates a new atom with "query". It internally uses Vanilla Client's `...query` procedure. import { atom, useAtom } from 'jotai' import { httpLink } from '@trpc/client' import { createTRPCJotai } from 'jotai-trpc' import { trpcPokemonUrl } from 'trpc-pokemon' import type { PokemonRouter } from 'trpc-pokemon' const trpc \= createTRPCJotai<PokemonRouter\>({ links: \[ httpLink({ url: trpcPokemonUrl, }), \], }) const NAMES = \[ 'bulbasaur', 'ivysaur', 'venusaur', 'charmander', 'charmeleon', 'charizard', 'squirtle', 'wartortle', 'blastoise', \] const nameAtom = atom(NAMES\[0\]) const pokemonAtom = trpc.pokemon.byId.atomWithQuery((get) => get(nameAtom)) const Pokemon = () => { const \[data, refresh\] \= useAtom(pokemonAtom) return ( <div\> <div\>ID: {data.id}</div\> <div\>Height: {data.height}</div\> <div\>Weight: {data.weight}</div\> <button onClick\={refresh}\>Refresh</button\> </div\> ) } #### Examples ### atomWithMutation `...atomWithMutation` creates a new atom with "mutate". It internally uses Vanilla Client's `...mutate` procedure. FIXME: add code example and codesandbox ### atomWithSubscription `...atomWithSubscription` creates a new atom with "subscribe". It internally uses Vanilla Client's `...subscribe` procedure. FIXME: add code example and codesandbox --- ## Page: https://jotai.org/docs/extensions/query TanStack Query provides a set of functions for managing async state (typically external data). From the Overview docs: > React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your React applications a breeze. jotai-tanstack-query is a Jotai extension library for TanStack Query. It provides a wonderful interface with all of the TanStack Query features, providing you the ability to use those features in combination with your existing Jotai state. ### Support jotai-tanstack-query currently supports TanStack Query v5. ### Install In addition to `jotai`, you have to install `jotai-tanstack-query` and `@tanstack/query-core` to use the extension. npm install jotai\-tanstack\-query @tanstack/query\-core ### Incremental Adoption You can incrementally adopt `jotai-tanstack-query` in your app. It's not an all or nothing solution. You just have to ensure you are using the same QueryClient instance. QueryClient Setup. const { data, isPending, isError } \= useQuery({ queryKey: \['todos'\], queryFn: fetchTodoList, }) const todosAtom \= atomWithQuery(() \=> ({ queryKey: \['todos'\], })) const \[{ data, isPending, isError }\] \= useAtom(todosAtom) ### Exported functions * `atomWithQuery` for useQuery * `atomWithInfiniteQuery` for useInfiniteQuery * `atomWithMutation` for useMutation * `atomWithSuspenseQuery` for useSuspenseQuery * `atomWithSuspenseInfiniteQuery` for useSuspenseInfiniteQuery * `atomWithMutationState` for useMutationState All functions follow the same signature. const dataAtom \= atomWithSomething(getOptions, getQueryClient) The first `getOptions` parameter is a function that returns an input to the observer. The second optional `getQueryClient` parameter is a function that return QueryClient. ### atomWithQuery usage `atomWithQuery` creates a new atom that implements a standard `Query` from TanStack Query. import { atom, useAtom } from 'jotai' import { atomWithQuery } from 'jotai-tanstack-query' const idAtom \= atom(1) const userAtom \= atomWithQuery((get) \=> ({ queryKey: \['users', get(idAtom)\], queryFn: async ({ queryKey: \[, id\] }) \=> { const res \= await fetch(\`https://jsonplaceholder.typicode.com/users/${id}\`) return res.json() }, })) const UserData \= () \=> { const \[{ data, isPending, isError }\] \= useAtom(userAtom) if (isPending) return <div\>Loading...</div\> if (isError) return <div\>Error</div\> return <div\>{JSON.stringify(data)}</div\> } ### atomWithInfiniteQuery usage `atomWithInfiniteQuery` is very similar to `atomWithQuery`, however it is for an `InfiniteQuery`, which is used for data that is meant to be paginated. You can read more about Infinite Queries here. > Rendering lists that can additively "load more" data onto an existing set of data or "infinite scroll" is also a very common UI pattern. React Query supports a useful version of useQuery called useInfiniteQuery for querying these types of lists. A notable difference between a standard query atom is the additional option `getNextPageParam` and `getPreviousPageParam`, which is what you'll use to instruct the query on how to fetch any additional pages. import { atom, useAtom } from 'jotai' import { atomWithInfiniteQuery } from 'jotai-tanstack-query' const postsAtom \= atomWithInfiniteQuery(() \=> ({ queryKey: \['posts'\], queryFn: async ({ pageParam }) \=> { const res \= await fetch(\`https://jsonplaceholder.typicode.com/posts?\_page=${pageParam}\`) return res.json() }, getNextPageParam: (lastPage, allPages, lastPageParam) \=> lastPageParam + 1, initialPageParam: 1, })) const Posts \= () \=> { const \[{ data, fetchNextPage, isPending, isError, isFetching }\] \= useAtom(postsAtom) if (isPending) return <div\>Loading...</div\> if (isError) return <div\>Error</div\> return ( <\> {data.pages.map((page, index) \=> ( <div key\={index}\> {page.map((post: any) \=> ( <div key\={post.id}\>{post.title}</div\> ))} </div\> ))} <button onClick\={() \=> fetchNextPage()}\>Next</button\> </\> ) } ### atomWithMutation usage `atomWithMutation` creates a new atom that implements a standard `Mutation` from TanStack Query. > Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. const postAtom \= atomWithMutation(() \=> ({ mutationKey: \['posts'\], mutationFn: async ({ title }: { title: string }) \=> { const res \= await fetch(\`https://jsonplaceholder.typicode.com/posts\`, { method: 'POST', body: JSON.stringify({ title, body: 'body', userId: 1, }), headers: { 'Content-type': 'application/json; charset=UTF-8', }, }) const data \= await res.json() return data }, })) const Posts \= () \=> { const \[{ mutate, status }\] \= useAtom(postAtom) return ( <div\> <button onClick\={() \=> mutate({ title: 'foo' })}\>Click me</button\> <pre\>{JSON.stringify(status, null, 2)}</pre\> </div\> ) } ### atomWithMutationState usage `atomWithMutationState` creates a new atom that gives you access to all mutations in the `MutationCache`. const mutationStateAtom \= atomWithMutationState((get) \=> ({ filters: { mutationKey: \['posts'\], }, })) ### Suspense jotai-tanstack-query can also be used with React's Suspense. ### atomWithSuspenseQuery usage import { atom, useAtom } from 'jotai' import { atomWithSuspenseQuery } from 'jotai-tanstack-query' const idAtom \= atom(1) const userAtom \= atomWithSuspenseQuery((get) \=> ({ queryKey: \['users', get(idAtom)\], queryFn: async ({ queryKey: \[, id\] }) \=> { const res \= await fetch(\`https://jsonplaceholder.typicode.com/users/${id}\`) return res.json() }, })) const UserData \= () \=> { const \[{ data }\] \= useAtom(userAtom) return <div\>{JSON.stringify(data)}</div\> } ### atomWithSuspenseInfiniteQuery usage import { atom, useAtom } from 'jotai' import { atomWithSuspenseInfiniteQuery } from 'jotai-tanstack-query' const postsAtom \= atomWithSuspenseInfiniteQuery(() \=> ({ queryKey: \['posts'\], queryFn: async ({ pageParam }) \=> { const res \= await fetch(\`https://jsonplaceholder.typicode.com/posts?\_page=${pageParam}\`) return res.json() }, getNextPageParam: (lastPage, allPages, lastPageParam) \=> lastPageParam + 1, initialPageParam: 1, })) const Posts \= () \=> { const \[{ data, fetchNextPage, isPending, isError, isFetching }\] \= useAtom(postsAtom) return ( <\> {data.pages.map((page, index) \=> ( <div key\={index}\> {page.map((post: any) \=> ( <div key\={post.id}\>{post.title}</div\> ))} </div\> ))} <button onClick\={() \=> fetchNextPage()}\>Next</button\> </\> ) } ### Referencing the same instance of Query Client in your project Perhaps you have some custom hooks in your project that utilises the `useQueryClient()` hook to obtain the `QueryClient` object and call its methods. To ensure that you reference the same `QueryClient` object, be sure to wrap the root of your project in a `<Provider>` and initialise `queryClientAtom` with the same `queryClient` value you provided to `QueryClientProvider`. Without this step, `useQueryAtom` will reference a separate `QueryClient` from any hooks that utilise the `useQueryClient()` hook to get the queryClient. Alternatively, you can specify your `queryClient` with `getQueryClient` parameter. #### Example In the example below, we have a mutation hook, `useTodoMutation` and a query `todosAtom`. We included an initialisation step in our root `<App>` node. Although they reference methods same query key (`'todos'`), the `onSuccess` invalidation in `useTodoMutation` will not trigger **if the `Provider` initialisation step was not done.** This will result in `todosAtom` showing stale data as it was not prompted to refetch. ⚠️ Note: When using **Typescript**, it is recommended to use a Map when passing the queryClient value to useHydrateAtoms. You can find a working example in the Initializing State on Render docs import { Provider } from 'jotai/react' import { useHydrateAtoms } from 'jotai/react/utils' import { useMutation, useQueryClient, QueryClient, QueryClientProvider, } from '@tanstack/react-query' import { atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' const queryClient \= new QueryClient() const HydrateAtoms \= ({ children }) \=> { useHydrateAtoms(\[\[queryClientAtom, queryClient\]\]) return children } export const App \= () \=> { return ( <QueryClientProvider client\={queryClient}\> <Provider\> { } <HydrateAtoms\> <App /> </HydrateAtoms\> </Provider\> </QueryClientProvider\> ) } export const todosAtom \= atomWithQuery((get) \=> { return { queryKey: \['todos'\], queryFn: () \=> fetch('/todos'), } }) export const useTodoMutation \= () \=> { const queryClient \= useQueryClient() return useMutation( async (body: todo) \=> { await fetch('/todo', { Method: 'POST', Body: body }) }, { onSuccess: () \=> { void queryClient.invalidateQueries(\['todos'\]) }, onError, } ) } ### SSR support All atoms can be used within the context of a server side rendered app, such as a next.js app or Gatsby app. You can use both options that React Query supports for use within SSR apps, hydration or `initialData`. ### Error handling Fetch error will be thrown and can be caught with ErrorBoundary. Refetching may recover from a temporary error. See a working example to learn more. ### Devtools In order to use the Devtools, you need to install it additionally. npm install @tanstack/react\-query\-devtools All you have to do is put the `<ReactQueryDevtools />` within `<QueryClientProvider />`. import { QueryClientProvider, QueryClient, QueryCache, } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools' import { queryClientAtom } from 'jotai-tanstack-query' const queryClient \= new QueryClient({ defaultOptions: { queries: { staleTime: Infinity, }, }, }) const HydrateAtoms \= ({ children }) \=> { useHydrateAtoms(\[\[queryClientAtom, queryClient\]\]) return children } export const App \= () \=> { return ( <QueryClientProvider client\={queryClient}\> <Provider\> <HydrateAtoms\> <App /> </HydrateAtoms\> </Provider\> <ReactQueryDevtools /> </QueryClientProvider\> ) } ## Migrate to v0.8.0 ### Change in atom signature All atom signatures have changed to be more consistent with TanStack Query. v0.8.0 returns only a single atom, instead of a tuple of atoms, and hence the name change from `atomsWithSomething` to`atomWithSomething`. \- const \[dataAtom, statusAtom\] \= atomsWithSomething(getOptions, getQueryClient) + const dataAtom \= atomWithSomething(getOptions, getQueryClient) ### Simplified Return Structure In the previous version of `jotai-tanstack-query`, the query atoms `atomsWithQuery` and `atomsWithInfiniteQuery` returned a tuple of atoms: `[dataAtom, statusAtom]`. This design separated the data and its status into two different atoms. #### atomWithQuery and atomWithInfiniteQuery * `dataAtom` was used to access the actual data (`TData`). * `statusAtom` provided the status object (`QueryObserverResult<TData, TError>`), which included additional attributes like `isPending`, `isError`, etc. In v0.8.0, they have been replaced by `atomWithQuery` and `atomWithInfiniteQuery` to return only a single `dataAtom`. This `dataAtom` now directly provides the `QueryObserverResult<TData, TError>`, aligning it closely with the behavior of Tanstack Query's bindings. To migrate to the new version, replace the separate `dataAtom` and `statusAtom` usage with the unified `dataAtom` that now contains both data and status information. \- const \[dataAtom, statusAtom\] \= atomsWithQuery(); \- const \[data\] \= useAtom(dataAtom); \- const \[status\] \= useAtom(statusAtom); + const dataAtom \= atomWithQuery(); + const \[{ data, isPending, isError }\] \= useAtom(dataAtom); #### atomWithMutation Similar to `atomsWithQuery` and `atomsWithInfiniteQuery`, `atomWithMutation` also returns a single atom instead of a tuple of atoms. The return type of the atom value is `MutationObserverResult<TData, TError, TVariables, TContext>`. \- const \[, postAtom\] \= atomsWithMutation(); \- const \[post, mutate\] \= useAtom(postAtom); + const postAtom \= atomWithMutation(); + const \[{ data, error, mutate }\] \= useAtom(postAtom); ### Examples #### Basic demo #### Devtools demo #### Hackernews --- ## Page: https://jotai.org/docs/extensions/urql urql offers a toolkit for GraphQL querying, caching, and state management. From the Overview docs: > urql is a highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow. It's built to be both easy to use for newcomers to GraphQL, and extensible, to grow to support dynamic single-app applications and highly customized GraphQL infrastructure. In short, urql prioritizes usability and adaptability. jotai-urql is a Jotai extension library for URQL. It offers a cohesive interface that incorporates all of URQL's GraphQL features, allowing you to leverage these functionalities alongside your existing Jotai state. ### Install You have to install `jotai-urql`, `@urql/core` and `wonka` to use the extension. npm install jotai\-urql @urql/core wonka ### Exported functions * `atomWithQuery` for client.query * `atomWithMutation` for client.mutation * `atomWithSubscription` for client.subscription ### Basic usage #### Query: import { useAtom } from 'jotai' const countQueryAtom \= atomWithQuery<{ count: number }\>({ query: 'query Count { count }', getClient: () \=> client, }) const Counter \= () \=> { const \[operationResult, reexecute\] \= useAtom(countQueryAtom) if (operationResult.error) { throw operationResult.error } return <\>{operationResult.data?.count}</\> } #### Mutation: import { useAtom } from 'jotai' const incrementMutationAtom \= atomWithMutation<{ increment: number }\>({ query: 'mutation Increment { increment }', }) const Counter \= () \=> { const \[operationResult, executeMutation\] \= useAtom(incrementMutationAtom) return ( <div\> <button onClick\={() \=> executeMutation().then((it) \=> console.log(it.data?.increment)) } \> Increment </button\> <div\>{operationResult.data?.increment}</div\> </div\> ) } ### Simplified type of options passed to functions type AtomWithQueryOptions< Data \= unknown, Variables extends AnyVariables \= AnyVariables, \> \= { query: DocumentInput<Data, Variables\> getVariables?: (get: Getter) \=> Variables getContext?: (get: Getter) \=> Partial<OperationContext\> getPause?: (get: Getter) => boolean getClient?: (get: Getter) => Client } type AtomWithMutationOptions< Data = unknown, Variables extends AnyVariables = AnyVariables, \> = { query: DocumentInput<Data, Variables\> getClient?: (get: Getter) \=> Client } // Subscription type is the same as AtomWithQueryOptions ### Disable suspense Usage of `import { loadable } from "jotai/utils"` is preferred instead as proven more stable. However is you still want that here is how you do it: import { suspenseAtom } from 'jotai-urql' export const App \= () \=> { useHydrateAtoms(\[\[suspenseAtom, false\]\]) return <Counter /> } ### Useful helper hook Here is the helper hook, to cover one rare corner case, and make use of these bindings similar to `@tanstack/react-query` default behavior where errors are treated as errors (in case of Promise reject) and are handled mostly in the nearby ErrorBoundaries. Only valid for suspended version. #### useQueryAtomData Neatly returns `data` after the resolution + handles all the error throwing/reexecute cases/corner cases. Note that Type is overridden so `data` it never `undefined` nor `null` (unless that's expected return type of the query itself) import type { AnyVariables, OperationResult } from '@urql/core' import { useAtom } from 'jotai' import type { AtomWithQuery } from 'jotai-urql' export const useQueryAtomData \= < Data \= unknown, Variables extends AnyVariables \= AnyVariables, \>( queryAtom: AtomWithQuery<Data, Variables\>, ) \=> { const \[opResult, dispatch\] \= useAtom(queryAtom) if (opResult.error && opResult.stale) { use( new Promise((resolve) \=> { setTimeout(resolve, 10000) }), ) } if (opResult.error) { throw opResult.error } if (!opResult.data) { throw Error( 'Query data is undefined. Probably you paused the query? In that case use \`useQueryAtom\` instead.', ) } return \[opResult.data, dispatch, opResult\] as \[ Exclude<typeof opResult.data, undefined\>, typeof dispatch, typeof opResult, \] } function use(promise: Promise<any\> | any) { if (promise.status \=== 'fulfilled') { return promise.value } if (promise.status \=== 'rejected') { throw promise.reason } else if (promise.status \=== 'pending') { throw promise } else { promise.status \= 'pending' ;(promise as Promise<any\>).then( (result: any) => { promise.status \= 'fulfilled' promise.value \= result }, (reason: any) => { promise.status \= 'rejected' promise.reason \= reason }, ) throw promise } } #### Basic demo ### Referencing the same instance of the client for both atoms and urql provider To ensure that you reference the same urqlClient object, be sure to wrap the root of your project in a `<Provider>` and initialise clientAtom with the same urqlClient value you provided to UrqlProvider. Without this step, you may end up specifying client each time when you use `atomWithQuery`. Now you can just ignore the optional `getClient` parameter, and it will use the client from the context. import { Suspense } from 'react' import { Provider } from 'jotai/react' import { useHydrateAtoms } from 'jotai/react/utils' import { clientAtom } from 'jotai-urql' import { createClient, cacheExchange, fetchExchange, Provider as UrqlProvider, } from 'urql' const urqlClient \= createClient({ url: 'https://countries.trevorblades.com/', exchanges: \[cacheExchange, fetchExchange\], fetchOptions: () \=> { return { headers: {} } }, }) const HydrateAtoms \= ({ children }) \=> { useHydrateAtoms(\[\[clientAtom, urqlClient\]\]) return children } export default function MyApp({ Component, pageProps }) { return ( <UrqlProvider value\={urqlClient}\> <Provider\> <HydrateAtoms\> <Suspense fallback\="Loading..."\> <Component {...pageProps} /> </Suspense\> </HydrateAtoms\> </Provider\> </UrqlProvider\> ) } --- ## Page: https://jotai.org/docs/extensions/effect jotai-effect is a utility package for reactive side effects in Jotai. ## Install npm install jotai\-effect ## observe `observe` mounts an `effect` to watch state changes on a Jotai `store`. It's useful for running global side effects or logic at the store level. If you don't have access to the store object and are not using the default store, use `atomEffect` or `withAtomEffect` instead. ### Signature type Cleanup \= () \=> void type Effect \= ( get: Getter & { peek: Getter } set: Setter & { recurse: Setter } ) \=> Cleanup | void type Unobserve \= () \=> void function observe(effect: Effect, store?: Store): Unobserve **effect:** A function for observing and reacting to atom state changes. **store:** A Jotai store to mount the effect on. Defaults to the global store if not provided. **returns:** A stable function that removes the effect from the store and cleans up any internal references. ### Usage import { observe } from 'jotai-effect' const unobserve \= observe((get, set) \=> { set(logAtom, \`someAtom changed: ${get(someAtom)}\`) }) unobserve() This allows you to run Jotai state-dependent logic outside React's lifecycle, ideal for application-wide effects. ### Usage With React Pass the store to both `observe` and the `Provider` to ensure the effect is mounted to the correct store. const store \= createStore() const unobserve \= observe((get, set) \=> { set(logAtom, \`someAtom changed: ${get(someAtom)}\`) }, store) <Provider store\={store}\>...</Provider\> ## atomEffect `atomEffect` creates an atom for declaring side effects that react to state changes when mounted. ### Signature function atomEffect(effect: Effect): Atom<void\> **effect:** A function for observing and reacting to atom state changes. ### Usage import { atomEffect } from 'jotai-effect' const logEffect \= atomEffect((get, set) \=> { set(logAtom, get(someAtom)) return () \=> { set(logAtom, 'unmounting') } }) function Component() { useAtom(logEffect) } ## withAtomEffect `withAtomEffect` binds an effect to a clone of the target atom. The effect is active while the cloned atom is mounted. ### Signature function withAtomEffect<T\>(targetAtom: Atom<T\>, effect: Effect): Atom<T\> **targetAtom:** The atom to which the effect is bound. **effect:** A function for observing and reacting to atom state changes. **Returns:** An atom that is equivalent to the target atom but having a bound effect. ### Usage import { withAtomEffect } from 'jotai-effect' const valuesAtom \= withAtomEffect(atom(null), (get, set) \=> { set(valuesAtom, get(countAtom)) return () \=> { } }) ## Dependency Management Aside from mount events, the effect runs when any of its dependencies change value. * **Sync:** All atoms accessed with `get` inside the effect are added to the atom's dependencies. Example atomEffect((get, set) \=> { get(anAtom) }) * **Async:** Asynchronous `get` calls do not add dependencies. Example atomEffect((get, set) \=> { setTimeout(() \=> { get(anAtom) }) }) * **Cleanup:** `get` calls in cleanup do not add dependencies. Example atomEffect((get, set) \=> { return () \=> { get(anAtom) } }) * **Dependency Map Recalculation:** Dependencies are recalculated on every run. Example atomEffect((get, set) \=> { if (get(isEnabledAtom)) { const aValue \= get(anAtom) } else { const anotherValue \= get(anotherAtom) } }) ## Effect Behavior * **Executes Synchronously:** `effect` runs synchronous in the current task after synchronous evaluations complete. Example const logCounts \= atomEffect((get, set) \=> { set(logAtom, \`count is ${get(countAtom)}\`) }) const actionAtom \= atom(null, (get, set) \=> { get(logAtom) set(countAtom, (value) \=> value + 1) get(logAtom) }) store.sub(logCounts, () \=> {}) store.set(actionAtom) * **Batched Updates:** Multiple synchronous updates are batched as a single atomic transaction. Example const tensAtom \= atom(0) const onesAtom \= atom(0) const updateTensAndOnes \= atom(null, (get, set) \=> { set(tensAtom, (value) \=> value + 1) set(onesAtom, (value) \=> value + 1) }) const combos \= atom(\[\]) const effectAtom \= atomEffect((get, set) \=> { const value \= get(tensAtom) \* 10 + get(onesAtom) set(combos, (arr) \=> \[...arr, value\]) }) store.sub(effectAtom, () \=> {}) store.set(updateTensAndOnes) store.get(combos) * **Resistant to Infinite Loops:** `atomEffect` avoids rerunning when it updates a value that it is watching. Example atomEffect((get, set) \=> { get(countAtom) set(countAtom, (value) \=> value + 1) }) * **Cleanup Function:** The cleanup function is invoked on unmount or before re-evaluation. Example atomEffect((get, set) \=> { const intervalId \= setInterval(() \=> set(clockAtom, Date.now())) return () \=> clearInterval(intervalId) }) * **Idempotency:** `atomEffect` runs once per state change, regardless of how many times it is referenced. Example let i \= 0 const effectAtom \= atomEffect(() \=> { get(countAtom) i++ }) store.sub(effectAtom, () \=> {}) store.sub(effectAtom, () \=> {}) store.set(countAtom, (value) \=> value + 1) console.log(i) * **Conditionally Running Effects:** `atomEffect` only runs when mounted. Example atom((get) \=> { if (get(isEnabledAtom)) { get(effectAtom) } }) * **Supports Peek:** Use `get.peek` to read atom data without subscribing. Example const countAtom \= atom(0) atomEffect((get, set) \=> { const count \= get.peek(countAtom) }) * **Supports Recursion:** Recursion is supported with `set.recurse` but not in cleanup. Example atomEffect((get, set) \=> { const count \= get(countAtom) if (count % 10 \=== 0) { return } set.recurse(countAtom, (value) \=> value + 1) }) --- ## Page: https://jotai.org/docs/extensions/immer ### Install You have to install `immer` and `jotai-immer` to use this feature. npm install immer jotai\-immer ## atomWithImmer `atomWithImmer` creates a new atom similar to the regular `atom` with a different `writeFunction`. In this bundle, we don't have read-only atoms, because the point of these functions is the immer produce(mutability) function. The signature of writeFunction is `(get, set, update: (draft: Draft<Value>) => void) => void`. import { useAtom } from 'jotai' import { atomWithImmer } from 'jotai-immer' const countAtom \= atomWithImmer({ value: 0 }) const Counter \= () \=> { const \[count\] \= useAtom(countAtom) return <div\>count: {count.value}</div\> } const Controls \= () \=> { const \[, setCount\] \= useAtom(countAtom) const inc \= () \=> setCount((draft) \=> { ++draft.value }) return <button onClick\={inc}\>+1</button\> } ### Examples Check this example with atomWithImmer: ## withImmer `withImmer` takes an atom and returns a derived atom, same as `atomWithImmer` it has a different `writeFunction`. import { useAtom, atom } from 'jotai' import { withImmer } from 'jotai-immer' const primitiveAtom \= atom({ value: 0 }) const countAtom \= withImmer(primitiveAtom) const Counter \= () \=> { const \[count\] \= useAtom(countAtom) return <div\>count: {count.value}</div\> } const Controls \= () \=> { const \[, setCount\] \= useAtom(countAtom) const inc \= () \=> setCount((draft) \=> { ++draft.value }) return <button onClick\={inc}\>+1</button\> } ### Examples Check this example with withImmer: ## useImmerAtom This hook takes an atom and replaces the atom's `writeFunction` with the new immer-like `writeFunction` like the previous helpers. import { atom } from 'jotai' import { useImmerAtom } from 'jotai-immer' const primitiveAtom \= atom({ value: 0 }) const Counter \= () \=> { const \[count\] \= useImmerAtom(primitiveAtom) return <div\>count: {count.value}</div\> } const Controls \= () \=> { const \[, setCount\] \= useImmerAtom(primitiveAtom) const inc \= () \=> setCount((draft) \=> { ++draft.value }) return <button onClick\={inc}\>+1</button\> } It would be better if you don't use `withImmer` and `atomWithImmer` with `useImmerAtom` because they provide the immer-like `writeFunction` and we don't need to create a new one. You can use `useSetImmerAtom` if you need only the setter part of `useImmerAtom`. ### Examples Check this example with useImmerAtom: ## Demo --- ## Page: https://jotai.org/docs/extensions/xstate Jotai's state management is primitive and flexible, but that sometimes means too free. XState is a sophisticated library to provide a better and safer abstraction for state management. ### Install You have to install `xstate` and `jotai-xstate` to use this feature. npm install xstate jotai\-xstate ## atomWithMachine `atomWithMachine` creates a new atom with XState machine. It receives a function `getMachine` to create a new machine. `getMachine` is invoked at the first use with `get` argument, with which you can read other atom values. import { useAtom } from 'jotai' import { atomWithMachine } from 'jotai-xstate' import { assign, createMachine } from 'xstate' const createEditableMachine \= (value: string) \=> createMachine<{ value: string }\>({ id: 'editable', initial: 'reading', context: { value, }, states: { reading: { on: { dblclick: 'editing', }, }, editing: { on: { cancel: 'reading', commit: { target: 'reading', actions: assign({ value: (\_, { value }) \=> value, }), }, }, }, }, }) const defaultTextAtom \= atom('edit me') const editableMachineAtom \= atomWithMachine((get) \=> createEditableMachine(get(defaultTextAtom)), ) const Toggle \= () \=> { const \[state, send\] \= useAtom(editableMachineAtom) return ( <div\> {state.matches('reading') && ( <strong onDoubleClick\={send}\>{state.context.value}</strong\> )} {state.matches('editing') && ( <input autoFocus defaultValue\={state.context.value} onBlur\={(e) \=> send({ type: 'commit', value: e.target.value })} onKeyDown\={(e) \=> { if (e.key \=== 'Enter') { send({ type: 'commit', value: e.target.value }) } if (e.key \=== 'Escape') { send('cancel') } }} /\> )} <br /> <br /> <div\> Double-click to edit. Blur the input or press <code\>enter</code\> to commit. Press <code\>esc</code\> to cancel. </div\> </div\> ) } ### Restartable machine stored in a global Provider (provider-less mode) When your machine reaches its final state it cannot receive any more events. If your atomWithMachine is initialized in global store (aka provider-less mode), to restart it you need to send a `RESTART` event to your machine like so: import { RESTART } from 'jotai-xstate' const YourComponent \= () \=> { const \[current, send\] \= useAtom(yourMachineAtom) const isFinalState \= current.matches('myFinalState') useEffect(() \=> { return () \=> { if (isFinalState) send(RESTART) } }, \[isFinalState\]) } ### Examples Check examples with atomWithMachine: Restartable machine: ### Tutorials Check out a course about Jotai and XState. Complex State Management in React with Jotai and XState (Note: In the course, it uses `jotai/xstate` which is supersede by `jotai-xstate`.) --- ## Page: https://jotai.org/docs/extensions/location To deal with `window.location`, we have some functions to create atoms. ### Install You have to install `jotai-location` to use this feature. npm install jotai\-location ## atomWithLocation atomWithLocation(options): PrimitiveAtom `atomWithLocation` creates a new atom that links to `window.location`. Typically, you should only instantiate `atomWithLocation` once per application. This is because changes made to one instance might not be reflected in others. As this atom is designed to synchronize with the `window.location` object, using multiple instances can lead to unpredictable behavior. ### Parameters **options** (optional): an object of options to customize the behavior of the atom ### Options **preloaded** (optional): preloaded location value to avoid getting location at initialization. **replace** (optional): a boolean to indicate to use `replaceState` instead of `pushState`. **getLocation** (optional): a custom function to get location value. **applyLocation** (optional): a custom function to set location value. **subscribe** (optional): a custom function to subscribe to location change. ### Examples import { useAtom } from 'jotai' import { atomWithLocation } from 'jotai-location' const locationAtom \= atomWithLocation() const App \= () \=> { const \[loc, setLoc\] \= useAtom(locationAtom) return ( <ul\> <li\> <button style\={{ fontWeight: loc.pathname \=== '/' ? 'bold' : 'normal', }} onClick\={() \=> setLoc((prev) \=> ({ ...prev, pathname: '/' }))} \> Home </button\> </li\> <li\> <button style\={{ fontWeight: loc.pathname \=== '/foo' && !loc.searchParams?.get('bar') ? 'bold' : 'normal', }} onClick\={() \=> setLoc((prev) \=> ({ ...prev, pathname: '/foo', searchParams: new URLSearchParams(), })) } \> Foo </button\> </li\> <li\> <button style\={{ fontWeight: loc.pathname \=== '/foo' && loc.searchParams?.get('bar') \=== '1' ? 'bold' : 'normal', }} onClick\={() \=> setLoc((prev) \=> ({ ...prev, pathname: '/foo', searchParams: new URLSearchParams(\[\['bar', '1'\]\]), })) } \> Foo?bar=1 </button\> </li\> </ul\> ) } ### Stackblitz ## atomWithHash atomWithHash(key, initialValue, options): PrimitiveAtom This creates a new atom that is connected with URL hash. The hash must be in the URLSearchParams format. It's a two-way binding: changing the atom value will change the hash and changing the hash will change the atom value. This function works only with DOM. ### 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 **options** (optional): an object of options to customize the behavior of the atom ### Options **serialize** (optional): a custom function to serialize the atom value to the hash. Defaults to `JSON.stringify`. **deserialize** (optional): a custom function to deserialize the hash to the atom value. Defaults to `JSON.parse`. **subscribe** (optional): custom hash change subscribe function **setHash** (optional): `replaceState` or a custom function that describes how hash gets updated on the side of the browser. Defaults to pushing a new entry to the history via `window.location.hash = searchParams` (jotai-location#4) ### Examples import { useAtom } from 'jotai' import { atomWithHash } from 'jotai-location' const countAtom \= atomWithHash('count', 1) const Counter \= () \=> { const \[count, setCount\] \= useAtom(countAtom) return ( <div\> <div\>count: {count}</div\> <button onClick\={() \=> setCount((c) \=> c + 1)}\>+1</button\> </div\> ) } ### Stackblitz ### Deleting Item Please refer atomWithStorage for the usage about deleting items. --- ## Page: https://jotai.org/docs/extensions/cache Jotai provides primitive functions to optimize re-renders. It's designed to hold only "current" atom values, and it doesn't cache older values. Caching is sometimes useful. For example, if an async atom triggers network requests, we may want to cache the responses. jotai-cache is a third-party library to help such use cases. ### Install npm install jotai\-cache ## atomWithCache atomWithCache(read, options): Atom `atomWithCache` creates a new read-only atom with cache. ### Parameters **read**: read function to define the read-only atom. **options** (optional): an object of options to customize the behavior of the atom ### Options **size** (optional): maximum size of cache items. **shouldRemove** (optional): a function to check if cache items should be removed. **areEqual** (optional): a function to compare atom values. ### Examples import { atom, useAtom } from 'jotai' import { atomWithCache } from 'jotai-cache' const idAtom \= atom(1) const normalAtom \= atom(async (get) \=> { const id \= get(idAtom) const response \= await fetch(\`https://reqres.in/api/users/${id}?delay=1\`) return response.json() }) const cachedAtom \= atomWithCache(async (get) \=> { const id \= get(idAtom) const response \= await fetch(\`https://reqres.in/api/users/${id}?delay=1\`) return response.json() }) const NormalUser \= () \=> { const \[{ data }\] \= useAtom(normalAtom) return ( <div\> <h1\>User (normal atom)</h1\> <ul\> <li\>ID: {data.id}</li\> <li\>First name: {data.first\_name}</li\> <li\>Last name: {data.last\_name}</li\> </ul\> </div\> ) } const CachedUser \= () \=> { const \[{ data }\] \= useAtom(cachedAtom) return ( <div\> <h1\>User (cached atom)</h1\> <ul\> <li\>ID: {data.id}</li\> <li\>First name: {data.first\_name}</li\> <li\>Last name: {data.last\_name}</li\> </ul\> </div\> ) } const App \= () \=> { const \[id, setId\] \= useAtom(idAtom) return ( <div\> ID: {id}{' '} <button type\="button" onClick\={() \=> setId((c) \=> c \- 1)}\> Prev </button\>{' '} <button type\="button" onClick\={() \=> setId((c) \=> c + 1)}\> Next </button\> <hr /> <Suspense fallback\="Loading..."\> <CachedUser /> </Suspense\> <hr /> <Suspense fallback\="Loading..."\> <NormalUser /> </Suspense\> </div\> ) } ### Stackblitz --- ## Page: https://jotai.org/docs/extensions/scope There are a few libraries to extend Jotai's usage in React. ## `jotai-scope` While Jotai's Provider allows to scope Jotai's store under a subtree, we can't use the store above the tree within the subtree. A workaround is to use `store` option in useAtom and other hooks. To ease such use cases, `jotai-scope` provides a scoped provider and hooks. Instead of specifying the `store` option, the scoped provider accepts `atoms` prop and the use of those atoms is scoped within the subtree. ### Install npm install jotai\-scope ### Counter Example import { atom, useAtom } from 'jotai' import { ScopeProvider } from 'jotai-scope' const countAtom \= atom(0) const anotherCountAtom \= atom(0) const Counter \= () \=> { const \[count, setCount\] \= useAtom(countAtom) const \[anotherCount, setAnotherCount\] \= useAtom(anotherCountAtom) return ( <\> <div\> <span\>count: {count}</span\> <button type\="button" onClick\={() \=> setCount((c) \=> c + 1)}\> increment </button\> </div\> <div\> <span\>another count: {anotherCount}</span\> <button type\="button" onClick\={() \=> setAnotherCount((c) \=> c + 1)}\> increment </button\> </div\> </\> ) } const App \= () \=> { return ( <div\> <h1\>First Provider</h1\> <ScopeProvider atoms\={\[anotherCountAtom\]}\> <Counter /> </ScopeProvider\> <h1\>Second Provider</h1\> <ScopeProvider atoms\={\[anotherCountAtom\]}\> <Counter /> </ScopeProvider\> </div\> ) } ## `createIsolation` Both Jotai's Provider and `jotai-scope`'s scoped provider are still using global contexts. If you are developing a library that depends on Jotai and the library user may use Jotai separately in their apps, they can share the same context. This can be troublesome because they point to unexpected Jotai stores. To avoid conflicting the contexts, a utility function called `createIsolation` is exported from `jotai-scope`. Check out the example in the repo: https://github.com/jotaijs/jotai-scope/tree/main/examples/01\_isolation ## `bunshi` (formerly `jotai-molecules`) Jotai atoms provide a basic solution to optimize re-renders. Atoms defined globally can depend on other atoms, but they can't depend on props and state within a component tree. It's possible to define atoms within a component tree, but then you would need to pass those atoms in some ways (for example, atoms-in-atom.) bunshi is a third-party library to help such use cases. See Motivation for more details. ### Install npm install bunshi ### Counter Example import { atom, useAtom } from 'jotai' import { molecule, useMolecule, createScope, ScopeProvider } from 'bunshi/react' const InitialCountScope \= createScope({ initialCount: 0 }) const countMolecule \= molecule((getMol, getScope) \=> { const { initialCount } \= getScope(InitialCountScope) return atom(initialCount) }) const Counter \= () \=> { const countAtom \= useMolecule(countMolecule) const \[count, setCount\] \= useAtom(countAtom) return ( <div\> {count} <button onClick\={() \=> setCount((c) \=> c + 1)}\>+1</button\> </div\> ) } const App \= () \=> ( <div\> <h1\>With initial value 1</h1\> <ScopeProvider scope\={InitialCountScope} value\={{ initialCount: 1 }}\> <Counter /> <Counter /> </ScopeProvider\> <h1\>With initial value 2</h1\> <ScopeProvider scope\={InitialCountScope} value\={{ initialCount: 2 }}\> <Counter /> <Counter /> </ScopeProvider\> <h1\>Default</h1\> <Counter /> <Counter /> </div\> ) --- ## Page: https://jotai.org/docs/extensions/optics ### Install You have to install `optics-ts` and `jotai-optics` to use this feature. npm install optics\-ts jotai\-optics ## focusAtom `focusAtom` creates a new atom, based on the focus that you pass to it. This creates a derived atom that will focus on the specified part of the atom, and when the derived atom is updated, the derivee is notified of the update, and the equivalent update is done on the derivee. See this: const baseAtom \= atom({ a: 5 }) const derivedAtom \= focusAtom(baseAtom, (optic) \=> optic.prop('a')) So basically, we started with a `PrimitiveAtom<{a: number}>`, which has a getter and a setter, and then used `focusAtom` to zoom in on the `a`\-property of the `baseAtom`, and got a `PrimitiveAtom<number>`. What is noteworthy here is that this `derivedAtom` is not only a getter, it is also a setter. If `derivedAtom` is updated, then equivalent update is done on the `baseAtom`. The example below is simple, but it's a starting point. `focusAtom` supports many kinds of optics, including `Lens`, `Prism`, `Isomorphism`. To see more advanced optics, please see the example at: https://github.com/akheron/optics-ts ### Example import { atom } from 'jotai' import { focusAtom } from 'jotai-optics' const objectAtom \= atom({ a: 5, b: 10 }) const aAtom \= focusAtom(objectAtom, (optic) \=> optic.prop('a')) const bAtom \= focusAtom(objectAtom, (optic) \=> optic.prop('b')) const Controls \= () \=> { const \[a, setA\] \= useAtom(aAtom) const \[b, setB\] \= useAtom(bAtom) return ( <div\> <span\>Value of a: {a}</span\> <span\>Value of b: {b}</span\> <button onClick\={() \=> setA((oldA) \=> oldA + 1)}\>Increment a</button\> <button onClick\={() \=> setB((oldB) \=> oldB + 1)}\>Increment b</button\> </div\> ) } #### Stackblitz --- ## Page: https://jotai.org/docs/extensions/redux Jotai's state resides in React, but sometimes it would be nice to interact with the world outside React. Redux provides a store interface that can be used to store some values and sync with atoms in Jotai. ### Install You have to install `redux` and `jotai-redux` to use this feature. npm install redux jotai\-redux ## atomWithStore `atomWithStore` creates a new atom with redux store. It's two-way binding and you can change the value from both ends. import { useAtom } from 'jotai' import { atomWithStore } from 'jotai-redux' import { createStore } from 'redux' const initialState \= { count: 0 } const reducer \= (state \= initialState, action: { type: 'INC' }) \=> { if (action.type \=== 'INC') { return { ...state, count: state.count + 1 } } return state } const store \= createStore(reducer) const storeAtom \= atomWithStore(store) const Counter \= () \=> { const \[state, dispatch\] \= useAtom(storeAtom) return ( <\> count: {state.count} <button onClick\={() \=> dispatch({ type: 'INC' })}\>button</button\> </\> ) } ### Examples --- ## Page: https://jotai.org/docs/extensions/relay You can use Jotai with Relay. ### Install You have to install `jotai-relay` and `relay-runtime`. npm install jotai\-relay relay\-runtime ### Usage See Relay Docs to learn about basics and how to use compiler in advance. ### atomWithQuery `atomWithQuery` creates a new atom with fetchQuery. import React, { Suspense } from 'react' import { Provider, useAtom } from 'jotai' import { useHydrateAtoms } from 'jotai/utils' import { environmentAtom, atomWithQuery } from 'jotai-relay' import { Environment, Network, RecordSource, Store } from 'relay-runtime' import graphql from 'babel-plugin-relay/macro' const myEnvironment \= new Environment({ network: Network.create(async (params, variables) \=> { const response \= await fetch('https://countries.trevorblades.com/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: params.text, variables, }), }) return response.json() }), store: new Store(new RecordSource()), }) const countriesAtom \= atomWithQuery( graphql\` query AppCountriesQuery { countries { name } } \`, () \=> ({}), ) const Main \= () \=> { const \[data\] \= useAtom(countriesAtom) return ( <ul\> {data.countries.map(({ name }) \=> ( <li key\={name}\>{name}</li\> ))} </ul\> ) } const HydrateAtoms \= ({ children }) \=> { useHydrateAtoms(\[\[environmentAtom, myEnvironment\]\]) return children } const App \= () \=> { return ( <Provider\> <HydrateAtoms\> <Suspense fallback\="Loading..."\> <Main /> </Suspense\> </HydrateAtoms\> </Provider\> ) } #### Examples ### atomWithMutation `atomWithMutation` creates a new atom with commitMutation. FIXME: add code example and codesandbox ### atomWithSubscription `atomWithSubscription` creates a new atom with requestSubscription. FIXME: add code example and codesandbox --- ## Page: https://jotai.org/docs/extensions/valtio Jotai's state resides in React, but sometimes it would be nice to interact with the world outside React. Valtio provides a proxy interface that can be used to store some values and sync with atoms in Jotai. This only uses the vanilla api of valtio. ### Install You have to install `valtio` and `jotai-valtio` to use this feature. npm install valtio jotai\-valtio ## atomWithProxy `atomWithProxy` creates a new atom with valtio proxy. It's two-way binding and you can change the value from both ends. import { useAtom } from 'jotai' import { atomWithProxy } from 'jotai-valtio' import { proxy } from 'valtio/vanilla' const proxyState \= proxy({ count: 0 }) const stateAtom \= atomWithProxy(proxyState) const Counter \= () \=> { const \[state, setState\] \= useAtom(stateAtom) return ( <\> count: {state.count} <button onClick\={() \=> setState((prev) \=> ({ ...prev, count: prev.count + 1 }))} \> inc atom </button\> <button onClick\={() \=> { ++proxyState.count }} \> inc proxy </button\> </\> ) } ### Parameters atomWithProxy(proxyObject, options?) **proxyObject** (required): the Valtio proxy object you want to derive the atom from **options.sync** (optional): makes the atom update synchronously instead of waiting for batched updates, similar to `valtio/useSnapshot`. This will result in more renders, but have more guarantees that it syncs with other Jotai atoms. atomWithProxy(proxyObject, { sync: true }) ### Examples ## mutableAtom `mutableAtom` wraps a value in a self-aware Valtio proxy. You can make changes to it in the same way you would to a normal js-object. Count value is stored under the `value` property. const countProxyAtom \= mutableAtom(0) function IncrementButton() { const countProxy \= useAtomValue(countProxyAtom) return <button onClick\={() \=> ++countProxy.value}\>+</button\> } ### Parameters mutableAtom(value, options?) **value** (required): the value to proxy. **options.proxyFn** (optional): allows customization with `proxyFn` for custom proxy functions. Can be `proxy` (default) or a custom function. ### Examples ### Caution on Mutating Proxies Be careful to not mutate the proxy directly in the atom's read function or during render. Doing so could cause an infinite render loop. const countProxyAtom \= mutableAtom(0) atom( (get) \=> { const countProxy \= get(countProxyAtom) ++countProxy.value }, (get, set) \=> { const countProxy \= get(countProxyAtom) ++countProxy.value }, ) --- ## Page: https://jotai.org/docs/extensions/zustand Jotai's state resides in React, but sometimes it would be nice to interact with the world outside React. Zustand provides a store interface that can be used to hold some values and sync with atoms in Jotai. This only uses the vanilla api of zustand. ### Install You have to install `zustand` and `jotai-zustand` to use this feature. npm install zustand jotai\-zustand ## atomWithStore `atomWithStore` creates a new atom with zustand store. It's two-way binding and you can change the value from both ends. import { useAtom } from 'jotai' import { atomWithStore } from 'jotai-zustand' import create from 'zustand/vanilla' const store \= create(() \=> ({ count: 0 })) const stateAtom \= atomWithStore(store) const Counter \= () \=> { const \[state, setState\] \= useAtom(stateAtom) return ( <\> count: {state.count} <button onClick\={() \=> setState((prev) \=> ({ ...prev, count: prev.count + 1 }))} \> button </button\> </\> ) } ### Examples --- ## Page: https://jotai.org/docs/basics/concepts Jotai is a library that will make you return to the basics of React development & keep everything simple. ### From scratch Before trying to compare Jotai with what we may have known previously, let's just dive straight into something very simple. The React world is very much like our world, it's a big set of small entities, we call them components, and we know that they have their own state. Structuring your components to interact altogether will create your app. Now, the Jotai world also has its small entities, atoms, and they also have their state. Composing atoms will create your app state! Jotai considers anything to be an atom, so you may say: `Huh, I need objects and arrays, filter them and then sort them out`. And here's the beauty of it, Jotai gracefully lets you create dumb atoms derived from even more dumb atoms. If, for example, I have a page with 2 tabs: online friends and offline friends. I will have 2 atoms simply derived from a common, dumber source. const dumbAtom \= atom(\[{ name: 'Friend 1', online: false }\]) const onlineAtom \= atom((get) \=> get(dumbAtom).filter((item) \=> item.online)) const offlineAtom \= atom((get) \=> get(dumbAtom).filter((item) \=> !item.online)) And you could keep going on complexity forever. Another incredible feature of Jotai is the built-in ability to suspend when using asynchronous atoms. This is a relatively new feature that needs more experimentation, but is definitely the future of how we will build React apps. Check out the docs for more info. --- ## Page: https://jotai.org/docs/basics/comparison Jotai was born to solve extra re-render issues in React. An extra re-render is when the render process produces the same UI result, where users won't see any differences. To tackle this issue with React context (useContext + useState), one would require many contexts and face some issues. * Provider hell: It's likely that your root component has many context providers, which is technically okay, and sometimes desirable to provide context in different subtree. * Dynamic addition/deletion: Adding a new context at runtime is not very nice, because you need to add a new provider and its children will be re-mounted. Traditionally, a top-down solution to this is to use a selector function. The use-context-selector library is one example. The issue with this approach is the selector function needs to return referentially equal values to prevent re-renders, and this often requires a memoization technique. Jotai takes a bottom-up approach with the atomic model, inspired by Recoil. One can build state combining atoms, and optimize renders based on atom dependency. This avoids the need for memoization. Jotai has two principles. * Primitive: Its basic API is simple, like `useState`. * Flexible: Atoms can derive another atom and form a graph. Atoms can also be updated by another arbitrary atom. It allows abstracting complex state models. ### How is Jotai different from useContext of React? Jotai's core API is minimalistic and makes it easy to build utilities based upon it. #### Analogy You can view Jotai as a drop-in replacement to `useContext`. Except Jotai is aiming for simplicity, minimalistic API and can do much more than `useContext` & `useState`. #### Usage difference We can see how we used to share data to children, compared to how we do it with Jotai. But let's use a real-world example where we have multiple `Context` in our app. const Component \= () \=> { const \[state, setState\] \= useState(0) } const StateContext \= createContext() const Parent \= ({ children }) \=> { return ( <StateContext.Provider value\={useState(0)}\> {children} </StateContext.Provider\> ) } const Component \= () \=> { const \[state, setState\] \= useContext(StateContext) } const State1Context \= createContext() const State2Context \= createContext() const Parent \= ({ children }) \=> ( <State1Context.Provider value\={useState(0)}\> <State2Context.Provider value\={useState(0)}\> {children} </State2Context.Provider\> </State1Context.Provider\> ) const Component1 \= () \=> { const \[state, setState\] \= useContext(State1Context) } const Component2 \= () \=> { const \[state, setState\] \= useContext(State2Context) } Now let's see how Jotai simplify it for us. You can just use atoms instead of multiple `Context`. import { Provider, atom, useAtom } from 'jotai' const atom1 \= atom(0) const atom2 \= atom(0) const Parent \= ({ children }) \=> { return <Provider\>{children}</Provider\> } const Component1 \= () \=> { const \[state, setState\] \= useAtom(atom1) } const Component2 \= () \=> { const \[state, setState\] \= useAtom(atom2) } ### How is Jotai different from Zustand? #### Name Jotai means "state" in Japanese. Zustand means "state" in German. #### Analogy Jotai is like Recoil. Zustand is like Redux. #### Where state resides To hold states, Both have stores that can exist either at module level or at context level. Jotai is designed to be context first, and module second. Zustand is designed to be module first, and context second. #### How to structure state Jotai state consists of atoms (i.e. bottom-up). Zustand state is one object (i.e. top-down). #### Technical difference The major difference is the state model. Zustand is a single store (although you could create multiple separate stores), while Jotai consists of primitive atoms and allows composing them together. In this sense, it's the matter of programming mental model. #### When to use which * If you need a replacement for useState+useContext, Jotai fits well. * If you want a simple module state, Zustand fits well. * If code splitting is important, Jotai should perform well. * If you prefer Redux devtools, Zustand is good to go. * If you want to make use of Suspense, Jotai is the one. ### How is Jotai different from Recoil? (Disclaimer: the author is not very familiar with Recoil, this may be biased and inaccurate.) #### Developer * Jotai is developed with collective work by a few developers in Poimandres (formerly react-spring) org. * Recoil is developed by a team at Facebook. #### Basis * Jotai is focusing on primitive APIs for easy learning, and it's unopinionated. (The same philosophy with Zustand) * Recoil is all-in-one, and it has various cache strategies. #### Technical difference * Jotai depends on atom object referential identities. * Recoil depends on atom string keys. #### When to use which * If you want to learn something new, either should work. * If you like Zustand, Jotai would also be pleasant. * If you need React Context alternatives, Jotai comes with enough features. * If you need to read and write atoms outside React, Jotai provides store API. * If you would try to create a new library, Jotai might give good primitives. * Otherwise, both are pretty similar about the general goals and basic techniques, so please try both and share your feedback with us. --- ## Page: https://jotai.org/docs/basics/showcase * Text Length example  Count the length and show the uppercase of any text. * Hacker News example  Demonstrate a news article with Jotai, hit next to see more articles. * Todos example  Record your todo list by typing them into this app, check them off if you have completed the task, and switch between `Completed` and `Incompleted` to see the status of your task. * Todos example with atomFamily and localStorage  Implement a todo list using atomFamily and localStorage. You can store your todo list to localStorage by clicking `Save to localStorage`, then remove your todo list and try restoring them by clicking `Load from localStorage`. * Clock with Next.js  An UTC time electronic clock implementation using Next.js and Jotai. * Tic Tac Toe game  A game of tic tac toe implemented with Jotai. * React Three Fiber demo  A simple demo to use Jotai with react-three-fiber --- ## Page: https://jotai.org/docs/basics/functional-programming-and-jotai ### Unexpected similarities If you look at getter functions long enough, you may see a striking resemblence to a certain JavaScript language feature. const nameAtom \= atom('Visitor') const countAtom \= atom(1) const greetingAtom \= atom((get) \=> { const name \= get(nameAtom) const count \= get(countAtom) return ( <div\> Hello, {name}! You have visited this page {count} times. </div\> ) }) Compare that code with `async`–`await`: const namePromise \= Promise.resolve('Visitor') const countPromise \= Promise.resolve(1) const greetingPromise \= (async function () { const name \= await namePromise const count \= await countPromise return ( <div\> Hello, {name}! You have visited this page {count} times. </div\> ) })() This similarity is no coincidence. Both atoms and promises are **Monads**†, a concept from functional programming. The syntax used in both `greetingAtom` and `greetingPromise` is known as **do-notation**, a syntax sugar for the plainer monad interface. ### About monads The monad interface is responsible for the fluidity of the atom and promise interfaces. The monad interface allowed us to define `greetingAtom` in terms of `nameAtom` and `countAtom`, and allowed us to define `greetingPromise` in terms of `namePromise` and `countPromise`. If you're curious, a structure (like `Atom` or `Promise`) is a monad if you can implement the following functions for it. A fun exercise is trying to implement `of`, `map` and `join` for Arrays. type SomeMonad<T\> = /\* for example... \*/ Array<T\> declare function of<T\>(plainValue: T): SomeMonad<T\> declare function map<T, V>( anInstance: SomeMonad<T\>, transformContents: (contents: T) => V, ): SomeMonad<V\> declare function join<T\>(nestedInstances: SomeMonad<SomeMonad<T\>\>): SomeMonad<T\> The shared heritage of Promises and Atoms means many patterns and best-practices can be reused between them. Let's take a look at one. ### Sequencing When talking about callback hell, we often mention the boilerplate, the indentation and the easy-to-miss mistakes. However, plumbing a single async operation into another single async operation was not the end of the callback struggle. What if we made four network calls and needed to wait for them all? A snippet like this was common: const nPending \= 4 const results: string\[\] function callback(err, data) { if (err) throw err results.push(data) if (results.length \=== nPending) { } } But what if the results have different types? and the order was important? Well, we'd have a lot more frustrating work to do! This logic would be duplicated at each usage, and would be easy to mess up. Since ES6, we simply call `Promise.all`: declare function promiseAll<T\>(promises: Array<Promise<T\>\>): Promise<Array<T\>\> `Promise.all` "rearranges" `Array` and `Promise`. It turns out this concept, _sequencing_, can be implemented for all monad–_Traversable_ pairs. Many kinds of collections are Traversables, including Arrays. For example, this is a case of sequencing specialized for atoms and arrays: function sequenceAtomArray<T\>(atoms: Array<Atom<T\>\>): Atom<Array<T\>\> { return atom((get) \=> atoms.map(get)) } ### Culmination Monads have been an interest to mathematicians for 60 years, and to programmers for 40. There are many resources out there on patterns for monads. Take a look at them! Here are a select few: * _Inventing Monads_ by Stepan Parunashvili * _How Monads Solve Problems_ by ThatsNoMoon * Wiki page list of monad tutorials * Typeclassopedia (for the curious) Learning a neat trick on using promises may well translate to atoms, as `Promise.all` and `sequenceAtomArray` did. Monads are not magic, just unusually useful, and a tool worth knowing. * * * _Notes_ **\[†\]** The ES6 Promise is not a completely valid monad because it cannot nest other Promises, e.g. `Promise<Promise<number>>` is semantically equivalent to `Promise<number>`. This is why Promises only have a `.then`, and not both a `.map` and `.flatMap`. ES6 Promises are probably more properly described as "monadic" rather than as monads. Unlike ES6 Promises, the ES6 Array is a completely lawful monad.