redux vs mobx vs vuex vs mobx-state-tree
Frontend State Management Libraries
reduxmobxvuexmobx-state-treeSimilar Packages:
Frontend State Management Libraries

mobx, mobx-state-tree, redux, and vuex are state management libraries designed to handle application data flow in frontend applications. mobx provides transparent reactive programming by making state observable and automatically tracking dependencies. mobx-state-tree builds on MobX with explicit, typed models, snapshots, and middleware for structured state trees. redux enforces a predictable, immutable state container using actions and pure reducer functions, often enhanced with middleware. vuex is a centralized state management pattern specifically for Vue.js applications, using mutations for synchronous state changes and actions for asynchronous logic. Note that Vuex is now in maintenance mode, with Pinia recommended for new Vue projects.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
redux18,002,72161,440290 kB482 years agoMIT
mobx2,499,05228,1544.35 MB804 months agoMIT
vuex1,486,94028,405271 kB143-MIT
mobx-state-tree108,7857,0541.29 MB105a year agoMIT

State Management in Modern JavaScript Apps: MobX, MST, Redux, and Vuex Compared

State management is a core challenge in frontend development. The libraries mobx, mobx-state-tree, redux, and vuex each offer distinct approaches to modeling, updating, and observing application state. While all solve the same fundamental problem — keeping UI in sync with data — their philosophies, APIs, and trade-offs differ significantly. Let’s compare them from an engineering perspective.

🧠 Core Philosophy: Reactive vs Predictable vs Component-Centric

mobx treats state as reactive objects. You write plain JavaScript classes or objects, decorate properties as observable, and let MobX automatically track which components depend on which values. Changes trigger re-renders only where needed.

// mobx
import { makeObservable, observable, action } from "mobx";

class Counter {
  count = 0;

  constructor() {
    makeObservable(this, {
      count: observable,
      increment: action
    });
  }

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

mobx-state-tree (MST) builds on MobX but enforces explicit models with types, snapshots, and actions. It combines immutability (via snapshots) with mutability (inside actions), offering runtime type checking and time-travel debugging.

// mobx-state-tree
import { types } from "mobx-state-tree";

const CounterModel = types
  .model({ count: 0 })
  .actions(self => ({
    increment() {
      self.count++;
    }
  }));

const counter = CounterModel.create();

redux follows a strict unidirectional data flow: state is immutable, updated only by pure reducer functions in response to dispatched actions. Everything is explicit — no magic tracking.

// redux
const initialState = { count: 0 };

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    default:
      return state;
  }
}

// Dispatch
store.dispatch({ type: 'INCREMENT' });

vuex is tightly integrated with Vue.js, using a centralized store with state, mutations (synchronous state changes), and actions (asynchronous logic). It leverages Vue’s reactivity system under the hood.

// vuex (Vue 2/3 Options API style)
const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => commit('increment'), 100);
    }
  }
});

⚠️ Important Note: As of 2024, Vuex is in maintenance mode. The official Vuex documentation states: “Vuex 4 will be the last version of Vuex. It has entered maintenance mode.” For new Vue 3 projects, the Vue team recommends Pinia instead. Therefore, vuex should not be used in new applications.

🔁 How State Changes Are Handled

Each library enforces different rules about how state can be modified.

  • mobx: Mutate observables directly inside action functions (or allow direct mutation if using configure({ enforceActions: "never" })). No immutability required.
// mobx: direct mutation allowed in actions
store.increment(); // modifies this.count in place
  • mobx-state-tree: State is mutable only inside actions. Outside actions, the tree is effectively immutable. All changes are recorded for devtools.
// MST: mutation only in actions
counter.increment(); // OK
// counter.count = 5; // throws error outside action
  • redux: State is always immutable. Reducers must return new objects. Libraries like Immer can help with ergonomic updates.
// redux with immer (common pattern)
import produce from "immer";

const reducer = produce((draft, action) => {
  if (action.type === 'INCREMENT') draft.count += 1;
}, initialState);
  • vuex: State changes must go through mutations, which are synchronous. This ensures devtools can track every change.
// vuex: commit mutation
store.commit('increment');

📡 Reactivity & Re-render Optimization

How each library connects state to UI components affects performance and developer experience.

  • mobx: Uses transparent reactivity. Components wrapped with observer automatically re-render when any observed value changes — no need to specify dependencies.
// mobx + React
import { observer } from "mobx-react-lite";

const CounterView = observer(({ counter }) => (
  <div>{counter.count}</div>
));
  • mobx-state-tree: Inherits MobX’s reactivity. Same observer pattern applies.

  • redux: Requires manual subscription or use of hooks like useSelector. You explicitly declare which state slices your component needs.

// redux + React
import { useSelector } from "react-redux";

const CounterView = () => {
  const count = useSelector(state => state.counter.count);
  return <div>{count}</div>;
};
  • vuex: In Vue, you access store state via this.$store (Options API) or useStore() (Composition API). Vue’s reactivity system handles updates automatically.
// vuex + Vue (Composition API)
import { computed } from 'vue';
import { useStore } from 'vuex';

export default {
  setup() {
    const store = useStore();
    const count = computed(() => store.state.count);
    return { count };
  }
};

🧪 Developer Experience: Debugging and Tooling

  • mobx: DevTools show which observables triggered which reactions. Less structured than Redux but very intuitive.

  • mobx-state-tree: Offers time-travel debugging, runtime type validation, and automatic snapshot serialization. Excellent for complex domain models.

// MST: get serializable snapshot
const json = getSnapshot(counter);
  • redux: Mature ecosystem with Redux DevTools that log every action, enable time travel, and inspect state diffs. Widely understood by teams.

  • vuex: Integrates with Vue DevTools, showing mutation history and state changes. However, since Vuex is deprecated, tooling investment has shifted to Pinia.

🧩 Composition and Scalability

  • mobx: Scales via class composition or module splitting. Easy to start, but large apps may lack structure without discipline.

  • mobx-state-tree: Designed for composable models. Trees can reference each other, support middleware, and enforce domain boundaries.

// MST: model composition
const Store = types.model({
  user: UserModel,
  todos: TodoListModel
});
  • redux: Scales via reducer composition (combineReducers) and middleware (e.g., Redux Toolkit simplifies this dramatically). Strong conventions prevent drift.

  • vuex: Supports modules for namespacing, but module boilerplate can feel heavy. Again, not recommended for new projects.

🔄 Interoperability and Ecosystem

  • mobx and mobx-state-tree work with any UI framework (React, Vue, Angular, Svelte) via thin bindings.

  • redux is framework-agnostic but most commonly used with React (via react-redux). Works anywhere you can call dispatch and getState.

  • vuex is exclusive to Vue.js. Cannot be used outside the Vue ecosystem.

✅ When to Use Which (Ignoring Vuex for New Projects)

ScenarioBest Fit
Rapid prototyping with minimal boilerplatemobx
Complex domain models requiring validation, snapshots, and time-travelmobx-state-tree
Large team needing strict conventions, auditability, and middlewareredux (preferably with Redux Toolkit)
New Vue 3 projectNot vuex — use Pinia instead

📊 Summary Table

Featuremobxmobx-state-treereduxvuex
Reactivity ModelTransparent trackingTransparent + snapshotsExplicit selectorsVue reactivity
State MutabilityMutable (in actions)Mutable (in actions only)ImmutableMutable (via mutations)
BoilerplateLowMediumMedium–High (lower with RTK)Medium
Type SafetyGood (with TS)Excellent (runtime + compile-time)Good (with TS)Fair
DevToolsMobX DevToolsMST DevTools + time travelRedux DevToolsVue DevTools
Framework Lock-inNoneNoneNoneVue only
Recommended for New Projects?✅ Yes✅ Yes✅ Yes❌ No (use Pinia)

💡 Final Guidance

  • If you value simplicity and reactivity and don’t mind mutable state, mobx gets you moving fast.
  • If you’re building a data-rich application (e.g., modeling financial instruments, healthcare records) and want safety nets, mobx-state-tree is unmatched.
  • If you work in a large team or regulated environment where traceability and predictability matter more than brevity, redux (with Redux Toolkit) remains a rock-solid choice.
  • Avoid vuex for new projects — the Vue team has moved on to Pinia, which offers a cleaner, more composable API.

Choose based on your team’s tolerance for magic vs. ceremony, your need for runtime guarantees, and whether you’re tied to a specific UI framework.

How to Choose: redux vs mobx vs vuex vs mobx-state-tree
  • redux:

    Choose redux—especially with Redux Toolkit—when working in large teams that benefit from strict conventions, comprehensive devtooling, middleware ecosystems (like Redux Thunk or Saga), and predictable, testable state transitions. It’s a mature choice for enterprise applications where auditability and consistency are critical.

  • mobx:

    Choose mobx when you need a lightweight, reactive state solution with minimal boilerplate and you're comfortable with mutable state. It’s ideal for small to medium applications or teams that prioritize developer velocity over strict architectural constraints. Its transparent reactivity works well with React, Vue, or any view layer.

  • vuex:

    Do not choose vuex for new projects. The Vue team has officially deprecated it in favor of Pinia, which offers a simpler, more composable, and TypeScript-friendly API. If maintaining a legacy Vue 2 or early Vue 3 app that already uses Vuex, continue using it—but migrate to Pinia when feasible.

  • mobx-state-tree:

    Choose mobx-state-tree when you require strong runtime guarantees, such as type safety, automatic serialization, time-travel debugging, and explicit action boundaries. It’s best suited for complex domain models (e.g., editors, dashboards, or data-intensive apps) where structure and maintainability outweigh the cost of additional setup.

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