redux vs mobx vs @ngrx/store vs @datorama/akita vs @ngneat/elf-state-history
State Management Libraries
reduxmobx@ngrx/store@datorama/akita@ngneat/elf-state-historySimilar Packages:
State Management Libraries

State management libraries are essential tools in modern web development, particularly in applications with complex state that needs to be shared across multiple components. These libraries provide a structured way to manage, update, and synchronize state, ensuring that the user interface reflects the current state of the application. They help in maintaining a single source of truth, making it easier to debug, test, and reason about the application's behavior. Popular state management libraries include Redux, MobX, Akita, and NgRx, each with its own approach to handling state, actions, and data flow.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
redux16,846,14161,397290 kB462 years agoMIT
mobx2,135,35028,0964.35 MB812 months agoMIT
@ngrx/store826,8038,269646 kB8125 days agoMIT
@datorama/akita42,8883,690343 kB483 years agoApache-2.0
@ngneat/elf-state-history6,7141,66637.4 kB92 years agoMIT
Feature Comparison: redux vs mobx vs @ngrx/store vs @datorama/akita vs @ngneat/elf-state-history

Architecture

  • redux:

    redux follows a predictable state container architecture that enforces a unidirectional data flow. It uses actions, reducers, and a single store to manage state, making it easy to track changes and implement time-travel debugging.

  • mobx:

    mobx uses a reactive programming model that automatically tracks dependencies and updates the UI when observables change. It allows for a more flexible and less structured approach to state management, reducing the need for boilerplate code while maintaining clear data flow.

  • @ngrx/store:

    @ngrx/store is based on the Redux pattern, promoting a unidirectional data flow and a clear separation of concerns. It uses actions, reducers, and selectors to manage state, making it easy to reason about state changes and integrate with Angular's dependency injection system.

  • @datorama/akita:

    @datorama/akita follows a simple architecture that separates state, actions, and queries. It encourages the use of entities and stores, making it easy to manage complex data structures while keeping the API intuitive and straightforward.

  • @ngneat/elf-state-history:

    @ngneat/elf-state-history is built on top of the Elf state management library, which provides a modular and composable architecture. It enhances the Elf architecture by adding history tracking capabilities, allowing for more advanced state management patterns.

Boilerplate Code

  • redux:

    redux is known for its boilerplate, especially in large applications where multiple actions and reducers are needed. However, tools like Redux Toolkit have been introduced to help reduce boilerplate and simplify the process of setting up a Redux store.

  • mobx:

    mobx significantly reduces boilerplate code compared to traditional state management libraries. Its reactive nature allows developers to define state and actions without the need for explicit bindings, making the code more concise and easier to understand.

  • @ngrx/store:

    @ngrx/store can introduce boilerplate due to its strict adherence to the Redux pattern, requiring the definition of actions, reducers, and selectors. However, this boilerplate is often justified by the clarity and maintainability it brings to large applications.

  • @datorama/akita:

    @datorama/akita minimizes boilerplate code by providing a simple API for creating stores and managing state. It reduces the need for complex setup and configuration, allowing developers to focus on building features rather than writing repetitive code.

  • @ngneat/elf-state-history:

    @ngneat/elf-state-history aims to keep boilerplate low by leveraging the modular nature of the Elf library. Its history tracking features are designed to be integrated with minimal additional code, making it easy to implement without overwhelming the developer with complexity.

Reactivity

  • redux:

    redux provides reactivity through the use of the connect function from React-Redux or similar libraries, which allows components to subscribe to state changes. However, the reactivity is not as fine-grained as in MobX, and components may re-render more than necessary unless optimized with selectors.

  • mobx:

    mobx is built around the concept of reactivity, automatically tracking changes to observables and updating the UI in response. It allows for fine-grained reactivity, meaning that only the parts of the UI that depend on a specific piece of state will update when that state changes, leading to more efficient rendering.

  • @ngrx/store:

    @ngrx/store integrates seamlessly with Angular's reactive programming model, providing reactivity through observables. Components can subscribe to state changes using selectors, ensuring that they only re-render when the relevant part of the state changes, which improves performance and reduces unnecessary updates.

  • @datorama/akita:

    @datorama/akita provides reactivity through its store and query system, allowing components to subscribe to state changes and automatically update when the state changes. It supports both manual and automatic updates, giving developers flexibility in how they manage reactivity.

  • @ngneat/elf-state-history:

    @ngneat/elf-state-history leverages the reactive capabilities of the Elf library to provide real-time updates to components when the state changes. Its history tracking feature adds an additional layer of reactivity, allowing components to respond to state changes in a more dynamic way.

Community and Ecosystem

  • redux:

    redux has one of the largest and most established communities in the JavaScript ecosystem. It is well-documented, and there are numerous tutorials, libraries, and middleware available. The ecosystem is mature, with many tools designed to work seamlessly with Redux, including Redux Toolkit, React-Redux, and more.

  • mobx:

    mobx has a large and active community, with many resources, tutorials, and third-party libraries available. Its flexible and unopinionated nature has led to a diverse ecosystem of tools and extensions that enhance its functionality and integrate with various frameworks.

  • @ngrx/store:

    @ngrx/store is part of the larger NgRx ecosystem, which is widely used in the Angular community. It has a large and active community, extensive documentation, and a wealth of third-party libraries and tools that integrate with NgRx, making it a mature and reliable choice for Angular applications.

  • @datorama/akita:

    @datorama/akita has a growing community and a rich ecosystem of plugins and extensions. Its focus on simplicity and ease of use has made it popular among developers looking for a lightweight yet powerful state management solution.

  • @ngneat/elf-state-history:

    @ngneat/elf-state-history is part of the Elf ecosystem, which is relatively new but rapidly gaining traction. The library is actively maintained, and its unique features are attracting attention from developers looking for more advanced state management solutions.

Ease of Use: Code Examples

  • redux:

    redux Example

    import { createStore } from 'redux';
    
    // Define initial state
    const initialState = { count: 0 };
    
    // Define reducer
    const reducer = (state = initialState, action) => {
      switch (action.type) {
        case 'INCREMENT':
          return { ...state, count: state.count + 1 };
        default:
          return state;
      }
    };
    
    // Create store
    const store = createStore(reducer);
    
    // Subscribe to state changes
    store.subscribe(() => {
      console.log('State:', store.getState());
    });
    
    // Dispatch action
    store.dispatch({ type: 'INCREMENT' });
    
  • mobx:

    mobx Example

    import { makeAutoObservable } from 'mobx';
    import { observer } from 'mobx-react';
    import React from 'react';
    
    // Create a store
    class Counter {
      count = 0;
    
      constructor() {
        makeAutoObservable(this);
      }
    
      increment() {
        this.count++;
      }
    }
    
    const counter = new Counter();
    
    // Create a React component
    const CounterComponent = observer(() => (
      <div>
        <p>Count: {counter.count}</p>
        <button onClick={() => counter.increment()}>Increment</button>
      </div>
    ));
    
    // Render the component
    ReactDOM.render(<CounterComponent />, document.getElementById('root'));
    
  • @ngrx/store:

    @ngrx/store Example

    import { Store } from '@ngrx/store';
    import { createAction, createReducer, on } from '@ngrx/store';
    
    // Define actions
    const increment = createAction('INCREMENT');
    
    // Define reducer
    const counterReducer = createReducer(0, on(increment, state => state + 1));
    
    // Create store
    const store = new Store(counterReducer);
    
    // Subscribe to state changes
    store.subscribe(state => console.log('State:', state));
    
    // Dispatch action
    store.dispatch(increment());
    
  • @datorama/akita:

    @datorama/akita Example

    import { createStore, createQuery } from '@datorama/akita';
    
    // Create a store
    const store = createStore({ count: 0 });
    
    // Create a query
    const query = createQuery(store);
    
    // Subscribe to state changes
    query.subscribe(state => console.log('State:', state));
    
    // Update state
    store.update({ count: 1 });
    
  • @ngneat/elf-state-history:

    @ngneat/elf-state-history Example

    import { createStore } from '@ngneat/elf';
    import { withHistory } from '@ngneat/elf-state-history';
    
    // Create a store with history tracking
    const store = createStore({ name: 'myStore' }, withHistory());
    
    // Subscribe to state changes
    store.subscribe(state => console.log('State:', state));
    
    // Update state
    store.update({ count: 1 });
    
How to Choose: redux vs mobx vs @ngrx/store vs @datorama/akita vs @ngneat/elf-state-history
  • redux:

    Choose redux if you need a predictable state container for JavaScript applications that follows a unidirectional data flow. It is ideal for large applications where state changes need to be managed in a controlled manner, and you want to leverage middleware for side effects, logging, and asynchronous actions.

  • mobx:

    Choose mobx if you prefer a reactive, less opinionated approach to state management that allows for fine-grained reactivity and automatic updates of the UI. It is suitable for applications where performance is critical, and you want to minimize the amount of boilerplate code required to manage state.

  • @ngrx/store:

    Choose @ngrx/store if you are building an Angular application and need a robust, scalable state management solution that follows the Redux pattern. It is ideal for large applications that require a clear separation of concerns, time-travel debugging, and integration with Angular's reactive programming model.

  • @datorama/akita:

    Choose @datorama/akita if you need a simple yet powerful state management solution that supports both local and global state with a focus on entities. It is ideal for applications that require a lightweight library with a clear structure and minimal boilerplate.

  • @ngneat/elf-state-history:

    Choose @ngneat/elf-state-history if you need a state management solution that emphasizes history tracking and undo/redo functionality. It is particularly useful for applications where tracking state changes and providing users with the ability to revert actions is important.

README for redux

Redux Logo

Redux is a predictable state container for JavaScript apps.

It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.

You can use Redux together with React, or with any other view library. The Redux core is tiny (2kB, including dependencies), and has a rich ecosystem of addons.

Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.

GitHub Workflow Status npm version npm downloads redux channel on discord

Installation

Create a React Redux App

The recommended way to start new apps with React and Redux Toolkit is by using our official Redux Toolkit + TS template for Vite, or by creating a new Next.js project using Next's with-redux template.

Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.

# Vite with our Redux+TS template
# (using the `degit` tool to clone and extract the template)
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app

# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-app

We do not currently have official React Native templates, but recommend these templates for standard React Native and for Expo:

npm install @reduxjs/toolkit react-redux

For the Redux core library by itself:

npm install redux

For more details, see the Installation docs page.

Documentation

The Redux core docs are located at https://redux.js.org, and include the full Redux tutorials, as well usage guides on general Redux patterns:

The Redux Toolkit docs are available at https://redux-toolkit.js.org, including API references and usage guides for all of the APIs included in Redux Toolkit.

Learn Redux

Redux Essentials Tutorial

The Redux Essentials tutorial is a "top-down" tutorial that teaches "how to use Redux the right way", using our latest recommended APIs and best practices. We recommend starting there.

Redux Fundamentals Tutorial

The Redux Fundamentals tutorial is a "bottom-up" tutorial that teaches "how Redux works" from first principles and without any abstractions, and why standard Redux usage patterns exist.

Help and Discussion

The #redux channel of the Reactiflux Discord community is our official resource for all questions related to learning and using Redux. Reactiflux is a great place to hang out, ask questions, and learn - please come and join us there!

Before Proceeding Further

Redux is a valuable tool for organizing your state, but you should also consider whether it's appropriate for your situation. Please don't use Redux just because someone said you should - instead, please take some time to understand the potential benefits and tradeoffs of using it.

Here are some suggestions on when it makes sense to use Redux:

  • You have reasonable amounts of data changing over time
  • You need a single source of truth for your state
  • You find that keeping all your state in a top-level component is no longer sufficient

Yes, these guidelines are subjective and vague, but this is for a good reason. The point at which you should integrate Redux into your application is different for every user and different for every application.

For more thoughts on how Redux is meant to be used, please see:

Basic Example

The whole global state of your app is stored in an object tree inside a single store. The only way to change the state tree is to create an action, an object describing what happened, and dispatch it to the store. To specify how state gets updated in response to an action, you write pure reducer functions that calculate a new state based on the old state and the action.

Redux Toolkit simplifies the process of writing Redux logic and setting up the store. With Redux Toolkit, the basic app logic looks like:

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    incremented: state => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decremented: state => {
      state.value -= 1
    }
  }
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
  reducer: counterSlice.reducer
})

// Can still subscribe to the store
store.subscribe(() => console.log(store.getState()))

// Still pass action objects to `dispatch`, but they're created for us
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

Redux Toolkit allows us to write shorter logic that's easier to read, while still following the original core Redux behavior and data flow.

Logo

You can find the official logo on GitHub.

Change Log

This project adheres to Semantic Versioning. Every release, along with the migration instructions, is documented on the GitHub Releases page.

License

MIT