Jotai takes an atomic approach to React state — bottom-up, composable, and incredibly efficient. No stores, no reducers, no selectors.
Atoms: The Building Blocks
import { atom, useAtom } from "jotai";
// Primitive atom
const countAtom = atom(0);
const nameAtom = atom("World");
// Derived atom (computed)
const doubledAtom = atom((get) => get(countAtom) * 2);
const greetingAtom = atom((get) => `Hello, ${get(nameAtom)}!`);
// Read-write derived atom
const celsiusAtom = atom(25);
const fahrenheitAtom = atom(
(get) => get(celsiusAtom) * 9/5 + 32,
(get, set, newF: number) => set(celsiusAtom, (newF - 32) * 5/9)
);
function Temperature() {
const [fahrenheit, setFahrenheit] = useAtom(fahrenheitAtom);
return <input value={fahrenheit} onChange={e => setFahrenheit(+e.target.value)} />;
}
Async Atoms
const urlAtom = atom("https://api.example.com/data");
const dataAtom = atom(async (get) => {
const url = get(urlAtom);
const res = await fetch(url);
return res.json();
});
function DataView() {
const [data] = useAtom(dataAtom); // Suspense-compatible!
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
atomWithStorage: Persistent State
import { atomWithStorage } from "jotai/utils";
const themeAtom = atomWithStorage("theme", "light");
const favoritesAtom = atomWithStorage<string[]>("favorites", []);
// Automatically syncs with localStorage
// Also syncs across browser tabs!
atomFamily: Dynamic Atom Collections
import { atomFamily } from "jotai/utils";
const productAtomFamily = atomFamily((id: string) =>
atom(async () => {
const res = await fetch(`/api/products/${id}`);
return res.json();
})
);
function Product({ id }: { id: string }) {
const [product] = useAtom(productAtomFamily(id));
return <div>{product.title}: ${product.price}</div>;
}
Integration: Jotai + TanStack Query
import { atomWithQuery } from "jotai-tanstack-query";
const productsAtom = atomWithQuery(() => ({
queryKey: ["products"],
queryFn: async () => {
const res = await fetch("/api/products");
return res.json();
},
staleTime: 5 * 60 * 1000,
}));
function Products() {
const [{ data, isLoading }] = useAtom(productsAtom);
if (isLoading) return <Spinner />;
return data.map(p => <ProductCard key={p.id} product={p} />);
}
splitAtom: Array Management
import { splitAtom } from "jotai/utils";
const itemsAtom = atom([{ id: 1, text: "Buy milk" }, { id: 2, text: "Walk dog" }]);
const itemAtomsAtom = splitAtom(itemsAtom);
function TodoList() {
const [itemAtoms, dispatch] = useAtom(itemAtomsAtom);
return (
<>
{itemAtoms.map((itemAtom) => (
<TodoItem key={`${itemAtom}`} itemAtom={itemAtom} />
))}
<button onClick={() => dispatch({ type: "insert", value: { id: 3, text: "New" } })}>
Add
</button>
</>
);
}
Atomic state for scraped data? My Apify tools deliver structured data for your Jotai atoms.
Custom solution? Email spinov001@gmail.com
Top comments (0)