DEV Community

Alex Spinov
Alex Spinov

Posted on

Jotai Has a Free Atomic State Library — Here's How to Use It

Global state doesn't have to be global. Jotai gives you atoms — tiny pieces of state that compose together. Only components reading an atom re-render when it changes.

What Is Jotai?

Jotai takes an atomic approach to React state management. Instead of one big store, you create independent atoms that components subscribe to individually. Think of it as "React useState, but shareable."

Quick Start

npm install jotai
Enter fullscreen mode Exit fullscreen mode
import { atom, useAtom } from 'jotai';

// Create atoms
const countAtom = atom(0);
const nameAtom = atom('World');

// Derived atom — automatically updates
const greetingAtom = atom((get) => `Hello, ${get(nameAtom)}! Count: ${get(countAtom)}`);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

function Name() {
  const [name, setName] = useAtom(nameAtom);
  return <input value={name} onChange={(e) => setName(e.target.value)} />;
}

function Greeting() {
  const [greeting] = useAtom(greetingAtom);
  return <p>{greeting}</p>; // Only re-renders when count OR name changes
}
Enter fullscreen mode Exit fullscreen mode

Atoms Are Composable

// Primitive atom
const usersAtom = atom([]);
const filterAtom = atom('');

// Derived atom — auto-computed
const filteredUsersAtom = atom((get) => {
  const users = get(usersAtom);
  const filter = get(filterAtom).toLowerCase();
  return users.filter(u => u.name.toLowerCase().includes(filter));
});

// Async atom — fetches data
const userDataAtom = atom(async () => {
  const res = await fetch('/api/users');
  return res.json();
});

// Write-only atom — actions
const addUserAtom = atom(null, (get, set, newUser) => {
  set(usersAtom, [...get(usersAtom), newUser]);
});
Enter fullscreen mode Exit fullscreen mode

Jotai vs. Zustand vs. Redux

Feature Jotai Zustand Redux
Mental model Atoms (bottom-up) Store (top-down) Store (top-down)
Re-renders Per-atom Per-selector Per-selector
Boilerplate Minimal Minimal Heavy
Best for Fine-grained state Simple global state Large teams
Async Built-in atoms Manual async Thunks/Sagas
DevTools Yes Yes Yes
React Suspense Native No No

Integrations

Persistence

import { atomWithStorage } from 'jotai/utils';

const themeAtom = atomWithStorage('theme', 'light');
// Automatically syncs with localStorage
Enter fullscreen mode Exit fullscreen mode

React Query Integration

import { atomWithQuery } from 'jotai-tanstack-query';

const userAtom = atomWithQuery(() => ({
  queryKey: ['user'],
  queryFn: async () => {
    const res = await fetch('/api/user');
    return res.json();
  },
}));
Enter fullscreen mode Exit fullscreen mode

Immer for Immutable Updates

import { atomWithImmer } from 'jotai-immer';

const todosAtom = atomWithImmer([]);

// Mutate directly — Immer handles immutability
const addTodoAtom = atom(null, (get, set, title) => {
  set(todosAtom, (draft) => {
    draft.push({ id: Date.now(), title, done: false });
  });
});
Enter fullscreen mode Exit fullscreen mode

Key Features

  • Atomic model — fine-grained re-renders
  • Suspense support — async atoms work with React Suspense
  • No providers needed (optional Provider for isolation)
  • TypeScript-first — full type inference
  • Tiny — 3KB bundle
  • Rich ecosystem — query, immer, storage, URQL, xstate

Get Started


Need fresh data atoms for your app? My Apify scrapers deliver structured web data. Custom solutions: spinov001@gmail.com

Top comments (0)