Primitive and flexible state management for React

Jotai mascot

No extra re-renders, state resides within React, you get the full benefits from suspense and concurrent features.

It’s scalable from a simple React.useState replacement to a large scale application with complex requirements.


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 derivatives
const textAtom = atom('hello')
const uppercaseAtom = atom((get) => get(textAtom).toUpperCase())
// Use them anywhere in your app
const Input = () => {
const [text, setText] = useAtom(textAtom)
return <input value={text} onChange={(e) => setText(} />
const Uppercase = () => {
const [uppercase] = useAtom(uppercaseAtom)
return <div>Uppercase: {uppercase}</div>
// Now you have the components
const MyApp = () => {
return (
<Input />
<Uppercase />

Extra utils

The Jotai package also includes a jotai/utils bundle. These functions add support for persisting an atom’s state in localStorage or a URL hash, creating an atom with a set function with redux-like reducers and action types, and more.
import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
// Set the string key and the initial value
const darkModeAtom = atomWithStorage('darkMode', false)
const Page = () => {
// Consume persisted state like any other atom
const [darkMode, setDarkMode] = useAtom(darkModeAtom)
return (
<h1>Welcome to {darkMode ? 'dark' : 'light'} mode!</h1>
<button onClick={() => setDarkMode(!darkMode)}>toggle theme</button>

Third-party integrations

There are also additional bundles for each official third-party integration. Immer, Optics, Query, XState, Valtio, Zustand, and Redux. Some integrations provide new atom types with alternate update functions such as atomWithImmer while others provide new atom types with two-way data binding for 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 function
const countAtom = atomWithImmer(0)
const Counter = () => {
const [count] = useAtom(countAtom)
return <div>count: {count}</div>
const Controls = () => {
// setCount === update: (draft: Draft<Value>) => void
const [, setCount] = useAtom(countAtom)
const increment = () => setCount((c) => (c = c + 1))
return <button onClick={increment}>+1</button>