Introduction: Navigating the Evolving Landscape of React State Management
In the dynamic world of React development, state management remains a central and often-debated topic. For years, the community has navigated a spectrum of solutions, from the powerful but verbose patterns of Redux to the built-in simplicity of the Context API. While each has its place, the quest for a solution that balances power, simplicity, and performance is perpetual. This ongoing evolution is a constant source of React News, with new libraries emerging to address the pain points of their predecessors. Enter Jotai, a state management library that has been steadily gaining traction for its minimalistic API and powerful, atomic approach.
Inspired by the concepts of Recoil but with a focus on a smaller core and zero dependencies outside of React, Jotai offers a fresh perspective. It eschews the boilerplate of traditional flux architectures and the performance pitfalls of a single, monolithic context. Instead, it provides a flexible, bottom-up approach where state is composed of small, independent pieces called “atoms.” This design not only simplifies state logic but also aligns perfectly with modern React features like Suspense and Concurrent Mode. For developers working with frameworks like Next.js, Remix, or Gatsby, or those exploring the latest in the Zustand News and Recoil News circles, Jotai presents a compelling alternative that promises less boilerplate and more intuitive, performant applications.
Section 1: Understanding the Core of Jotai – The Power of Atoms
At the heart of Jotai is a single, powerful concept: the atom. An atom represents a piece of state—it can be a primitive value like a number or a string, an object, or an array. Unlike a centralized store seen in Redux News, Jotai encourages you to create many small, distributed atoms that hold specific pieces of information. This atomic model is the foundation of Jotai’s performance benefits, as components subscribe only to the specific atoms they need, preventing unnecessary re-renders.
The Basic Atom and the useAtom
Hook
Creating and using an atom is remarkably straightforward. The library exposes a primary function, atom()
, to define a piece of state. You then use the `useAtom` hook, which has a familiar API similar to React’s `useState`, to interact with this atom from within your components.
Let’s look at a simple counter example:
import { atom, useAtom } from 'jotai';
// 1. Define an atom. This can be done in a separate file (e.g., store.js).
// This atom holds a numeric value, initialized to 0.
export const countAtom = atom(0);
// 2. Create a component that uses the atom.
function Counter() {
// useAtom returns a tuple [value, setValue], just like useState.
const [count, setCount] = useAtom(countAtom);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
<button onClick={() => setCount((c) => c - 1)}>Decrement</button>
</div>
);
}
export default Counter;
In this snippet, countAtom
is defined outside the component, making it globally accessible to any component that imports it. The Counter
component uses useAtom(countAtom)
to subscribe to its state. When the `setCount` function is called, only the components that use `countAtom` will re-render.
Derived Atoms: Composing State with Ease
Jotai’s real power shines with derived atoms. A derived atom computes its value based on one or more other atoms. This allows you to create a graph of dependent states that automatically update when their dependencies change. This is a concept that developers familiar with selectors in Redux or computed properties in MobX News will appreciate.
A derived atom is created by passing a function to `atom()`. This function receives a `get` argument, which it uses to read the value of other atoms.

import { atom, useAtom } from 'jotai';
// Our primitive "source" atom from the previous example
export const countAtom = atom(0);
// A read-only derived atom.
// It depends on countAtom and computes a new value.
export const isEvenAtom = atom((get) => {
const count = get(countAtom);
return count % 2 === 0;
});
// A component that only cares about whether the count is even.
function EvenStatus() {
// We use useAtom to read the value of the derived atom.
const [isEven] = useAtom(isEvenAtom);
return <p>Is the count even? {isEven ? 'Yes' : 'No'}</p>;
}
// When the count in the Counter component changes, this component
// will re-render automatically, but only when the `isEven` value
// actually changes (from true to false or vice versa).
export default EvenStatus;
This pattern is incredibly powerful. The `EvenStatus` component is completely decoupled from the `countAtom` itself; it only knows about `isEvenAtom`. Jotai’s dependency tracking ensures that when `countAtom` changes, `isEvenAtom` is re-evaluated, and only then does `EvenStatus` re-render if the derived value has changed. This granular reactivity is a core tenet of the latest Jotai News and a key reason for its excellent performance.
Section 2: Asynchronous Operations and Actions
Modern web applications are inherently asynchronous. Fetching data, handling user input, and managing loading states are daily challenges. Jotai provides elegant, first-class support for asynchronous operations, integrating seamlessly with React Suspense to create smooth and declarative loading experiences. This is a significant area of innovation, often discussed in React Query News and Apollo Client News, and Jotai offers a lightweight, integrated solution.
Async Atoms with React Suspense
An atom can be defined with an `async` function. When a component tries to read the value of such an atom, Jotai will automatically suspend the component’s rendering until the promise resolves. This allows you to use React’s `
Here’s how you can create an atom to fetch user data from an API:
import { atom, useAtom } from 'jotai';
import { Suspense } from 'react';
// An atom that fetches a random user from an API
const userAtom = atom(async () => {
const response = await fetch('https://randomuser.me/api/');
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const data = await response.json();
return data.results[0];
});
// A component to display the user's name
function UserProfile() {
// When this component first renders, useAtom(userAtom) will trigger
// the async function. Because it returns a promise, Jotai will
// suspend this component.
const [user] = useAtom(userAtom);
return (
<div>
<h2>User Profile</h2>
<p>Name: {user.name.first} {user.name.last}</p>
<p>Email: {user.email}</p>
</div>
);
}
// The main App component wraps the async component in Suspense
function App() {
return (
<div>
<h1>My Application</h1>
<Suspense fallback={<p>Loading user data...</p>}>
<UserProfile />
</Suspense>
</div>
);
}
export default App;
This code is clean, declarative, and free of manual `isLoading`, `error`, and `data` states. React Suspense handles the loading UI, and if the fetch fails, the promise rejection can be caught by a React Error Boundary. This pattern is particularly useful in modern frameworks like those featured in Next.js News and Remix News, which heavily leverage Suspense for server-side rendering and data fetching.
Write-only Atoms for Actions
Sometimes, you need to define logic that updates state without having a readable value itself. These are often called “actions.” In Jotai, you can create a write-only atom by passing `null` as the first argument to the derived atom’s function and providing a write function as the second argument. This is perfect for encapsulating business logic.
import { atom, useAtom } from 'jotai';
const valueAtom = atom(10);
// A write-only atom that defines an "action"
const multiplyAtom = atom(
null, // First argument is null because this is a write-only atom
(get, set, factor) => {
// 'get' allows reading other atoms
const currentValue = get(valueAtom);
// 'set' allows writing to other atoms
set(valueAtom, currentValue * factor);
}
);
function ValueManager() {
const [value] = useAtom(valueAtom);
// We only need the "setter" function for our action atom
const [, multiply] = useAtom(multiplyAtom);
return (
<div>
<p>Current Value: {value}</p>
<button onClick={() => multiply(2)}>Double the Value</button>
<button onClick={() => multiply(3)}>Triple the Value</button>
</div>
);
}
Here, `multiplyAtom` doesn’t hold a value itself but provides a function to update `valueAtom`. This pattern helps separate state logic from UI components, making your codebase cleaner and more maintainable, a principle often championed in discussions around form management libraries like React Hook Form News.
Section 3: Advanced Techniques and Patterns
As you build more complex applications, you’ll encounter scenarios that require more sophisticated state management patterns. Jotai’s ecosystem includes utilities that address these needs without sacrificing its core simplicity. These advanced features are what make Jotai a viable contender in complex projects, often discussed alongside news from the Blitz.js News or RedwoodJS News communities.
Managing Dynamic Lists with `atomFamily`

A common challenge is managing the state of individual items in a list. Creating a separate atom for each item manually is not feasible. The `atomFamily` utility solves this by acting as a factory for atoms. You provide it with an initialization function that takes a parameter, and it returns a function that you can call with that parameter to get or create a specific atom.
Let’s model a simple to-do list where each item has its own “completed” state:
import { atom, useAtom } from 'jotai';
import { atomFamily } from 'jotai/utils';
// A list of todo IDs
const todoIdsAtom = atom([1, 2, 3]);
// An atom family to manage the state of each individual todo.
// The first function defines how to create an atom for a given parameter (id).
// The second function compares parameters for equality.
const todoAtomFamily = atomFamily(
(id) => atom({ id, text: `Task ${id}`, completed: false }),
(a, b) => a === b
);
// Component for a single todo item
function TodoItem({ id }) {
const [todo, setTodo] = useAtom(todoAtomFamily(id));
const toggleCompleted = () => {
setTodo({ ...todo, completed: !todo.completed });
};
return (
<li style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text} <button onClick={toggleCompleted}>Toggle</button>
</li>
);
}
// Component to display the list of todos
export default function TodoList() {
const [todoIds] = useAtom(todoIdsAtom);
return (
<ul>
{todoIds.map((id) => (
<TodoItem key={id} id={id} />
))}
</ul>
);
}
With `atomFamily`, each `TodoItem` component subscribes only to its own state. Toggling one item will not cause any other items in the list to re-render, leading to highly performant dynamic lists. This is a significant advantage over storing the entire list in a single state object.
Integrating with the React Ecosystem
Jotai is not an island; it’s designed to work seamlessly with the broader React ecosystem. Its atoms can be used to store and sync state with other libraries. For instance, you can use Jotai to manage the state of a complex form alongside Formik News, or sync URL query parameters with a library like the one covered in React Router News. Its compatibility with testing frameworks like React Testing Library News and Jest News is also excellent, as atoms can be initialized with specific values in test environments, making components easy to isolate and test.
Section 4: Best Practices and Performance Optimization
While Jotai is performant by default, following best practices can help you get the most out of the library and avoid common pitfalls. These considerations are crucial for building scalable and maintainable applications, whether you’re using Vite, as seen in Vite News, or building a mobile app with React Native, a topic frequently covered in Expo News and React Native News.

Best Practices for Structuring Atoms
- Define Atoms Globally: Always define your atoms outside of your component render functions. Creating an atom inside a component will create a new instance on every render, leading to bugs and performance issues.
- Keep Atoms Small: Embrace the atomic philosophy. Prefer many small, focused atoms over a few large, monolithic ones. This maximizes performance by ensuring components only re-render when the exact piece of state they care about changes.
- Colocate When Possible: For feature-specific state, it can be helpful to define the atoms in the same file or directory as the components that use them. This improves modularity and makes the code easier to reason about.
Optimizing Renders with Specialized Hooks
The `useAtom` hook is a convenient all-in-one solution, but Jotai provides more specialized hooks for cases where a component doesn’t need both the value and the setter. Using these can prevent unnecessary re-renders.
useAtomValue(atom)
: Use this hook when a component only needs to read the value of an atom. The component will not re-render if only the setter function of the atom changes (which is rare but possible with certain advanced patterns).useSetAtom(atom)
: Use this hook when a component only needs to update an atom’s value but doesn’t need to read it. This component will never re-render when the atom’s value changes. This is perfect for components that only trigger actions.
These optimizations, combined with Jotai’s inherent granular updates, make it a powerful tool for building highly responsive user interfaces, including complex animations with libraries like React Native Reanimated News or Framer Motion News.
Conclusion: Why Jotai is a Strong Contender for Your Next Project
Jotai has carved out a significant niche in the crowded field of React state management. By embracing a minimalistic, atomic model, it offers a developer experience that is both simple and profoundly powerful. Its core strengths—a tiny API, automatic performance optimization through granular subscriptions, first-class TypeScript support, and seamless integration with React Suspense—make it an excellent choice for modern application development.
For teams looking to move away from the complexity of Redux or seeking a more performant alternative to the standard Context API, Jotai provides a clear and compelling path forward. Its bottom-up approach encourages better architecture and scales gracefully from small projects to large, complex applications. As the React ecosystem continues to evolve, particularly with advancements in frameworks like Next.js and tools like those in the Cypress News and Storybook News spheres, Jotai stands out as a forward-looking library that is perfectly in sync with the future of React. The next time you start a new project, consider giving Jotai a try; you might find it’s the refreshingly simple and powerful solution you’ve been looking for.