Introduction
Jotai takes a bottom-up approach to React state management with an atomic model inspired by Recoil. One can build state by combining atoms and renders are optimized based on atom dependency. This solves the extra re-render issue of React context and eliminates the need for the memoization technique.
Core API
Jotai has a very minimal API and is TypeScript oriented. It is as simple to use as React’s integrated useState
hook, but all state is globally accessible, derived state is easy to implement, and extra re-renders are automatically eliminated.
import { atom, useAtom } from 'jotai'// Create your atoms and derivativesconst textAtom = atom('hello')const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())// Use them anywhere in your appconst Input = () => {const [text, setText] = useAtom(textAtom)const handleChange = (e) => setText(e.target.value)return (<input value={text} onChange={handleChange} />)}const Uppercase = () => {const [uppercase] = useAtom(uppercaseAtom)return (<div>Uppercase: {uppercase}</div>)}// Now you have the componentsconst App = () => {return (<><Input /><Uppercase /></>)}
Extra utilities
The Jotai package also includes a jotai/utils
bundle. These functions add support for persisting an atom’s state in localStorage (or URL hash), hydrating an atom’s state during server-side rendering, creating an atom with a set function including Redux-like reducers and action types, and much more!
import { useAtom } from 'jotai'import { atomWithStorage } from 'jotai/utils'// Set the string key and the initial valueconst darkModeAtom = atomWithStorage('darkMode', false)const Page = () => {// Consume persisted state like any other atomconst [darkMode, setDarkMode] = useAtom(darkModeAtom)const toggleDarkMode = () => setDarkMode(!darkMode)return (<><h1>Welcome to {darkMode ? 'dark' : 'light'} mode!</h1><button onClick={toggleDarkMode}>toggle theme</button></>)}
Third-party integrations
There are also additional bundles for each official third-party integration. Immer, Optics, Query, XState, Valtio, Zustand, Redux, and URQL.
Some integrations provide new atom types with alternate set functions such as atomWithImmer
while others provide new atom types with two-way data binding with other state management libraries such as atomWithStore
which is bound with a Redux store.
import { useAtom } from 'jotai'import { atomWithImmer } from 'jotai/immer'// Create a new atom with an immer-based write functionconst countAtom = atomWithImmer(0)const Counter = () => {const [count] = useAtom(countAtom)return (<div>count: {count}</div>)}const Controls = () => {// setCount === update: (draft: Draft<Value>) => voidconst [, setCount] = useAtom(countAtom)const increment = () => setCount((c) => (c = c + 1))return (<button onClick={increment}>+1</button>)}