MobX logo

MobX

Battle-tested, transparent functional reactive programming (TFRP) state management.

npm install mobx
28.1K2.2M/weekv6.15.04.14 MBMIT78 issues
Last updated: 2025-09-26
Star history chart for mobxjs/mobx

TL;DR

A battle-tested library that makes state management simple and scalable by applying transparent functional reactive programming (TFRP).

It allows you to manage application state outside of the React tree, automatically tracking dependencies and updating only the components that actually changed.

Why MobX?

MobX approaches state management with a unique philosophy: anything that can be derived from the application state, should be. Unlike Redux's explicit dispatching or Zustand's hook-based selection, MobX feels "magical" because it tracks data usage at runtime.

  • Transparent Reactivity: MobX automatically tracks which data is used in your components and triggers re-renders only when that specific data changes.
  • Computed Values: Define complex derived data (like filtered lists or totals) that update automatically and are cached until their dependencies change.
  • Mutable Mental Model: You can modify state directly (e.g., store.todos.push(...)) while MobX handles the immutable updates and notifications under the hood.
  • Framework Agnostic: The core logic is independent of React, making it easy to test your business logic in isolation without mocking UI components.
  • Scalability: Because subscriptions are fine-grained, performance often remains high by default without manual optimization techniques like memo or selectors.

Code Snippet

Modern MobX uses makeAutoObservable to drastically reduce boilerplate, making classes feel like plain JavaScript objects.

import { makeAutoObservable } from "mobx";
import { observer } from "mobx-react-lite";

// 1. Define the Store
class TimerStore {
  seconds = 0;

  constructor() {
    // Automatically marks properties as observable, 
    // getters as computed, and methods as actions
    makeAutoObservable(this);
  }

  increase() {
    this.seconds += 1;
  }

  reset() {
    this.seconds = 0;
  }

  // Computed property (cached)
  get timeDescription() {
    return `${this.seconds} seconds passed`;
  }
}

const myTimer = new TimerStore();

// 2. Wrap Component with observer
const TimerView = observer(({ timer }) => (
  <div>
    <span>{timer.timeDescription}</span>
    <button onClick={() => timer.increase()}>Tick</button>
    <button onClick={() => timer.reset()}>Reset</button>
  </div>
));

export default function App() {
  return <TimerView timer={myTimer} />;
}

The observer wrapper turns the React component into a reactive derivation of the data. It tracks which observables are used during render and automatically re-renders the component when those specific values change.

Pros and Cons

No library is perfect; understanding the trade-offs is key to selecting the right tool.

Pros

  • Fine-Grained Performance: Components only re-render if the exact data they access changes, often eliminating the need for React.memo.
  • Intuitive for OOP: Developers with backgrounds in Object-Oriented Programming often find MobX's class-based model more natural than functional reducers.
  • Powerful Derived State: Computed values are first-class citizens, solving complex dependency chains effortlessly.

Cons

  • The "Magic" Factor: Because MobX abstracts away the subscription mechanism, debugging why a component didn't update can sometimes be confusing for beginners.
  • Loose Structure: Unlike Redux, MobX is unopinionated about how you structure your stores, which can lead to spaghetti code in large teams without strict conventions.
  • React Integration: You must remember to wrap every component that reads state with observer, or it simply won't update.

Comparison with Other State Management Libraries

The table below outlines the positioning differences between MobX and other popular State Management libraries to help you make an informed decision:

LibraryDesign PhilosophyBest ForPain Points
MobXReactive OOP
State is mutable and observable; updates propagate automatically via dependency tracking.
Complex Domains
Applications with heavy business logic and complex data relationships.
Too Flexible
Easy to create messy state mutations without strict architectural discipline.
ReduxFlux/Immutable
Single source of truth with explicit dispatching and pure reducer functions.
Enterprise Apps
Large teams requiring strict predictability, traceability, and debugging tools.
Boilerplate
Requires writing more code (actions, reducers) to achieve simple tasks.
ZustandMinimalist Hook
A small, unopinionated state manager that uses hooks for direct access.
General Purpose
Most modern React apps that need global state without the complexity.
Manual Selectors
Optimization requires manually selecting slices of state to prevent re-renders.

Verdict: When to Adopt

MobX shines in applications with complex domain models where data is highly interconnected. If you find yourself writing excessive boilerplate in Redux or struggling with manual optimization dependencies in Context/Zustand, MobX's "spreadsheet" model of automatic reactivity provides a significant productivity boost.