5 Alternatives to Redux for React State Management
Tags: react, redux, webdev, frontend
Published: 2024-04-05 15:07:39

A good state management solution is foundational to building a frontend application with any amount of complexity beyond “*Hello World!*”. One of the most well-known state management libraries for React is Redux, but there are many alternatives that may suit you better, depending on your needs and taste.

Here are 5 popular alternatives to Redux in the React universe that offer user-friendly APIs and have been hardened with real-world usage. Importantly, they are also actively maintained, with all having an update in the last month (as of this writing in April 2024).

For consistency and ease of understanding, I’ve demonstrated each library's capabilities using a simple counter example. Let’s go!

## Zustand

https://github.com/pmndrs/zustand

Github stars: 42k

Last updated: March 2024

Supports TypeScript: yes

Zustand keeps things simple and straightforward for managing state in React. You can accomplish most basic tasks by using just one export, `create`, which exposes your store as a hook.

```jsx
import { create } from 'zustand';

const useStore = create(set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 })),
}));

function Counter() {
  const { count, increment, decrement } = useStore();
  return (
    <div>
      <button onClick={decrement}>-</button>
      {count}
      <button onClick={increment}>+</button>
    </div>
  );
}
```

### Use Zustand when…

1. **You need to build rapidly.** Thanks to its simplicity and minimal setup, Zustand is perfect for rapid prototyping. Developers can quickly implement state management and make adjustments on the fly without getting bogged down by complex configurations.
2. **You need a flexible architecture.** Zustand does not enforce a strict architecture, allowing developers to structure their state however they see fit. This flexibility is beneficial in projects that require a unique or unconventional state management setup.

## Jotai

https://github.com/pmndrs/jotai

Github stars: 17k

Last updated: March 2024

Supports TypeScript: yes

Jotai provides a simple and flexible state management solution, using “atoms” for managing global state that's both easy to use and scalable. Atoms represent pieces of state that you can compose to achieve the desired complexity.

```jsx
import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

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

### Use Jotai when…

1. **You can naturally divide your state.** Jotai's atom-based approach is great for projects that benefit from fine-grained control over state. Each atom can be independently updated and composed, making it an excellent choice for applications where state needs to be managed in small, reusable pieces.
2. **Your project emphasizes immutability.**  Jotai’s API encourages the use of pure functions and immutable data, aligning with modern React development practices.

## MobX

https://github.com/mobxjs/mobx

Github stars: 27k

Last updated: April 2024

Supports TypeScript: yes

MobX offers fine-grained observability and automatic tracking of state changes for React components using Functional Reactive Programming (FRP).

FRP (a comprehensive topic by itself!) allows you to declaratively specify your state and its changes over time. Using FRP, MobX tries to make state management as transparent and predictable as possible. You can read more about the principles of MobX [here](https://mobx.js.org/the-gist-of-mobx.html).

```jsx
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';

// Class-based store
class CounterStore {
    count: number = 0;

    constructor() {
        makeAutoObservable(this);
    }

    increment() {
        this.count += 1;
    }

    decrement() {
        this.count -= 1;
    }
}

const counterStore = new CounterStore();

const Counter = observer(() => (
  <div>
    <button onClick={() => counterStore.decrement()}>-</button>
    {counterStore.count}
    <button onClick={() => counterStore.increment()}>+</button>
  </div>
));
```

You can also create the store with a functional paradigm:

```jsx
// Function-based store
function createCounterStore() {
  return makeAutoObservable({
    count: 0,
    increment() {
      this.count += 1;
    },
    decrement() {
      this.count -= 1;
    }
  });
};

const counterStore = createCounterStore();
```
### Use MobX when…

1. **You like object-oriented patterns.** MobX’s API is designed to work seamlessly with classes (such as ES6 React classes) and objects, making it a natural fit for applications that use OOP principles extensively.
2. **Your project requires fine-grained reactivity.** MobX gives you the ability to precisely control when your components re-render in response to state changes. You can ensure that only components that depend on changed observables are re-rendered, optimizing performance.

## XState

https://github.com/statelyai/xstate

Github stars: 26k

Last updated: March 2024

Supports TypeScript: yes

XState leverages state machines and statecharts for managing state, which is especially useful for complex state logic and workflows. The initial state is set with the `initial` key, and allowed state transitions are set within the `on` key of each state within `states`.

```jsx
import { useMachine } from '@xstate/react';
import { createMachine, assign } from 'xstate';

const counterMachine = createMachine({
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      on: {
        INCREMENT: { actions: assign({ count: ctx => ctx.count + 1 }) },
        DECREMENT: { actions: assign({ count: ctx => ctx.count - 1 }) },
      }
    }
  }
});

function Counter() {
  const [state, send] = useMachine(counterMachine);
  return (
    <div>
      <button onClick={() => send('DECREMENT')}>-</button>
      {state.context.count}
      <button onClick={() => send('INCREMENT')}>+</button>
    </div>
  );
}
```

### Use XState when…

1. **Your UI logic can be represented as a state machine.** XState shines in applications that benefit from modeling their UI logic as state machines or statecharts. Think apps with complex state logic, multiple states, and transitions, such as forms or games.
2. **You want to visualize your logic.** XState comes with tooling that allows you to visualize your state machines and workflows. This feature is invaluable during both development and debugging phases, especially for large apps.

## Valtio

https://github.com/pmndrs/valtio

Github stars: 8k

Last updated: March 2024

Supports TypeScript: yes

Valtio simplifies state management by using a proxy state. Create the state with `proxy()` and use it just like a JavaScript object!

It simplifies state mutations and ensures React components automatically update in response. Simply call `useSnapshot` with your state object.

```jsx
import { proxy, useSnapshot } from 'valtio';

const state = proxy({ count: 0 });

function Counter() {
  const snapshot = useSnapshot(state);
  return (
    <div>
      <button onClick={() => state.count--}>-</button>
      {snapshot.count}
      <button onClick={() => state.count++}>+</button>
    </div>
  );
}
```

### Use Valtio when…

1. **You need rapid development.** Valtio’s lack of boilerplate makes it easy to move fast. It’s ideal for prototypes and small to medium sized projects.
2. **Your app requires mutable state**. Valtio provides an easy and efficient way to manage state mutations directly, without the overhead of immutability patterns.

## Conclusion

These examples illustrate the diverse approaches to state management in React, offering solutions ranging from minimalistic and straightforward (like Zustand and Jotai) to more structured and complex (like XState). Valtio provides an easy way to manage state through direct mutations, while MobX focuses on reactive state changes through observables. Each library has its strengths and is suitable for different scenarios depending on the requirements of your React application.

Which of these libraries to you prefer? Do you use one that I left out? Let me know in the comments!

*PS. I share content like this post 2x / week. If you’re interested in frontend, backend, and how to become a better full stack developer,* [subscribe to my newsletter](https://jaydevm.hashnode.dev/newsletter)*!*