flux, redux, alt, mobx, and vuex are libraries designed to manage application state in complex frontend applications, primarily for React and Vue ecosystems.
flux is the original architectural pattern introduced by Facebook, enforcing a strict unidirectional data flow but lacking a concrete implementation, serving more as a concept. redux evolved from Flux to provide a single, predictable state container with pure reducers, becoming the industry standard for React for many years. alt is a wrapper around the Flux pattern that reduces boilerplate by automatically generating action creators and stores, aiming to make Flux easier to adopt. mobx takes a fundamentally different approach, using observable data structures and reactive derivations to automatically update the UI when state changes, favoring mutable code over immutability. vuex is the official state management pattern and library for Vue.js, integrating deeply with Vue's reactivity system to provide a centralized store with strict mutation rules.
While flux and alt are largely historical or niche today, redux, mobx, and vuex remain critical choices depending on whether you are building with React or Vue and your preference for functional vs. object-oriented patterns.
Managing state in modern frontend applications is one of the most critical architectural decisions you will make. The choices between redux, mobx, vuex, and the older flux/alt patterns define how your data flows, how easy your code is to debug, and how much boilerplate your team must write daily. Let's break down how these tools actually work under the hood.
The biggest split in this group is between the Flux family (flux, redux, alt, vuex) and MobX.
The Flux Family enforces a strict one-way street. Data moves in a circle: Action → Dispatcher → Store → View. You cannot change state directly; you must send a message (action) describing what happened. This makes debugging easier because every change has a clear paper trail.
mobx throws out the strict rules. It treats state like a normal spreadsheet. You change a value directly, and the library automatically figures out which parts of the UI need to update. It feels more like standard object-oriented programming.
// redux: Dispatch an action to change state (Indirect)
store.dispatch({ type: 'INCREMENT', payload: 1 });
// mobx: Change state directly (Direct)
store.count = store.count + 1;
How much code do you need to write before you can store a single number? The difference here is massive.
flux (the original Facebook package) requires you to manually create a Dispatcher, define Action Constants, create Action Creators, and register Stores. It is very verbose and rarely used directly today.
// flux: Manual Dispatcher setup
var Dispatcher = require('flux').Dispatcher;
var dispatcher = new Dispatcher();
// You must manually register callbacks for every store
dispatcher.register(function(payload) {
// Handle logic here
});
alt tried to fix this by auto-generating action creators. You define a class, and it handles the wiring. It was a great bridge, but the ecosystem moved on.
// alt: Auto-generated actions
class UserActions {
loadUser(id) {
this.dispatch(id);
}
}
// Alt automatically creates the action creator for you
redux strikes a middle ground. You write pure functions (reducers) that specify how state changes. It requires more setup than MobX but offers extreme predictability.
// redux: Pure reducer function
function counter(state = 0, action) {
if (action.type === 'INCREMENT') {
return state + 1;
}
return state;
}
mobx is the most concise. You simply decorate your class properties as observable.
// mobx: Observable class
import { makeAutoObservable } from "mobx";
class Counter {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
}
vuex is tailored for Vue. It uses a specific structure with state, mutations (synchronous changes), and actions (async logic). It is stricter than MobX but more integrated than Redux.
// vuex: Store definition
const store = createStore({
state: { count: 0 },
mutations: {
increment(state) { state.count++ }
},
actions: {
increment({ commit }) { commit('increment') }
}
});
This is the most controversial technical difference. How do you actually change the data?
redux, flux, and alt demand immutability. You never change the existing state object. You create a new copy with the changes. This allows tools to quickly check if data changed by comparing references.
// redux: Return a NEW object
return {
...state,
user: { ...state.user, name: "Alice" }
};
mobx and vuex (inside mutations) allow mutability. You change the properties directly. MobX tracks these changes using JavaScript Proxies (or getters/setters in older versions) to trigger updates.
// mobx: Direct mutation
state.user.name = "Alice"; // UI updates automatically
// vuex: Direct mutation inside a committed function
mutations: {
setName(state, newName) {
state.user.name = newName;
}
}
Real apps need to fetch data from servers. Since state updates are usually synchronous, how do these tools handle delays?
redux does not handle async logic by default. You must add "middleware" like redux-thunk or redux-saga. This keeps the core small but adds complexity.
// redux + thunk: Action creator returns a function
const fetchData = () => async (dispatch) => {
dispatch({ type: 'LOADING' });
const data = await api.get();
dispatch({ type: 'SUCCESS', payload: data });
};
alt and flux often handle async inside the Action Creators themselves, dispatching a "start" action, then a "success" action later.
// alt/flux pattern: Async in action creator
loadUser(id) {
this.dispatch(); // Start
api.getUser(id).then(user => {
this.dispatch(user); // Success
});
}
mobx doesn't care. Since you can change state anytime, you just use standard async/await inside your class methods or React components.
// mobx: Standard async/await
async loadUser(id) {
this.isLoading = true;
const user = await api.getUser(id);
this.user = user;
this.isLoading = false;
}
vuex separates concerns strictly. mutations must be synchronous. actions handle the async work and then commit mutations.
// vuex: Actions handle async, Mutations handle change
actions: {
async loadUser({ commit }, id) {
const user = await api.getUser(id);
commit('setUser', user);
}
}
Before choosing, you must know the current status of these packages.
flux and alt are effectively deprecated for new greenfield projects. The flux package on npm is a reference implementation, not a production framework. alt has seen minimal maintenance in recent years. The React community has consolidated around Redux (for strict flows) or Context/MobX (for simpler needs).
redux is actively maintained and widely used, though the team now recommends using Redux Toolkit to reduce boilerplate. The core redux package is stable but low-level.
mobx is actively maintained and very popular for teams who dislike Redux's verbosity.
vuex is the standard for Vue 2 and Vue 3, though the Vue team now recommends Pinia for new Vue 3 projects as it offers a simpler API and better TypeScript support. However, vuex remains fully supported for existing apps.
Despite their fights, these libraries share common goals and patterns.
Even MobX encourages a one-way flow of data logic, even if the implementation differs. All these tools try to prevent the "spaghetti code" of two-way binding where data changes unpredictably.
// All patterns aim for this flow:
// Event -> Logic -> State Update -> UI Refresh
They all move state out of individual components into a single "Store" or "Container." This makes sharing data between distant components easy.
// Concept shared by all:
const globalState = { user: null, theme: 'dark' };
// Any component can access globalState
Redux, MobX, and Vuex all have excellent browser extensions that let you inspect state changes over time, a huge win over local component state.
// All support time-travel debugging or state inspection
// via browser extensions (Redux DevTools, Vue Devtools, MobX React DevTools)
| Feature | redux | mobx | vuex | flux / alt |
|---|---|---|---|---|
| Philosophy | Functional, Immutable | OOP, Mutable | Integrated, Strict | Original Pattern |
| Boilerplate | High (without Toolkit) | Low | Medium | Very High |
| Learning Curve | Steep | Gentle | Moderate | Steep |
| Async Handling | Middleware (Thunk/Saga) | Native Async/Await | Actions + Mutations | Action Creators |
| Ecosystem | Massive | Strong | Vue-Specific | Legacy |
| Best For | Large, Complex React Apps | Rapid Dev, Complex Relations | Vue.js Applications | Legacy Maintenance |
redux is the enterprise standard. If you are building a massive React application with hundreds of developers, strict rules and immutability will save you from chaos. It forces discipline.
mobx is the productivity booster. If you want to write less code and prefer thinking in objects and classes, MobX gets out of your way. It is perfect for dashboards or apps with complex, interconnected data models.
vuex (or its successor Pinia) is the only logical choice for Vue.js. It is baked into the framework's lifecycle. Do not try to force Redux into a Vue app unless you have a very specific reason.
flux and alt belong in the history books. They solved problems that Redux and MobX have since solved better. Only touch them if you are maintaining an older codebase.
Final Thought: The "best" library depends on your team's culture. Do you value strict rules and explicit traces (redux)? Or do you value speed and natural coding styles (mobx)? For Vue, the choice is made for you (vuex/Pinia). Avoid the legacy Flux packages for any new work.
Choose redux if you need a predictable, time-travel-debuggable state container with a strict unidirectional data flow. It is the best fit for large-scale React applications where explicit state transitions, middleware ecosystems (like Redux Saga/Thunk), and strong community support are critical for long-term maintenance.
Choose mobx if you prefer an object-oriented approach where state is mutable and updates happen automatically via observables. It is ideal for teams who find Redux boilerplate excessive and want a less verbose way to handle complex, interconnected state without writing explicit reducers or action types.
Choose vuex if you are building an application with Vue.js (versions 2 or 3). It is the official, integrated solution that leverages Vue's reactivity system. Do not use it for React projects; for Vue 3 specifically, consider its successor Pinia for a more modern API, but vuex remains the standard for existing Vue ecosystems.
Avoid choosing alt for new projects. While it successfully reduced Flux boilerplate, the broader ecosystem has moved toward Redux or MobX for React. It is only relevant if you are maintaining a legacy codebase that specifically relies on its automatic action generation features.
Do not choose the flux package for new development. It is an reference implementation of a pattern, not a robust library for production use. The community has largely standardized on Redux (for strict unidirectional flow) or MobX (for reactive simplicity) instead of the original Flux architecture.
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.
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.
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.
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.
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.
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!
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:
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:
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.
You can find the official logo on GitHub.
This project adheres to Semantic Versioning. Every release, along with the migration instructions, is documented on the GitHub Releases page.