Introduction to MobX with React

Master simple, scalable state management for React applications. Learn how MobX's reactive system eliminates boilerplate while keeps your UI in sync.

State management is one of the most critical decisions when building React applications. While React's built-in useState and useContext hooks work well for simple cases, applications with complex, interconnected state often require a dedicated solution. MobX offers a refreshingly simple approach: instead of boilerplate reducers and action dispatchers, you define observable state and let MobX automatically track dependencies and update your UI.

In this guide, we'll explore how MobX works, why it differs from alternatives like Redux, and how to integrate it effectively with React components. Whether you're building a dashboard with real-time updates or a complex form wizard, understanding MobX gives you a powerful tool for managing state without the overhead of more prescriptive solutions.

For teams building modern web applications with Next.js, MobX provides a path to clean state management without the ceremony that larger libraries demand. Combined with our React development services, it forms part of a robust architecture for scalable applications.

What is MobX?

MobX is a simple, scalable, and battle-tested state management library that fundamentally changes how you think about state in your applications. At its core, MobX is based on a simple principle: make everything that can be derived from your application state derive automatically. This philosophy, borrowed from spreadsheet logic, means you declare what state exists, and MobX handles the rest--automatically computing derived values, updating components when relevant data changes, and ensuring your UI always reflects the current state without manual wiring.

The library takes a declarative approach to state management. Rather than writing explicit subscription logic or reducer functions that specify how state transitions should occur, you mark certain properties as observable and let MobX's reactivity system track which parts of your code depend on that state. When an observable value changes, MobX efficiently determines exactly which derived values and components need updating, then performs those updates automatically.

What sets MobX apart from alternatives like Redux is its minimal boilerplate and intuitive API. Redux requires you to define action types, action creators, reducers, and often middleware for async operations--creating a significant amount of infrastructure code even for simple features. MobX, by contrast, lets you start with plain JavaScript objects and classes, adding reactivity through simple decorators or function calls.

As noted in the MobX documentation, this approach treats your application like a spreadsheet where cells automatically update dependent formulas when their values change.

Key MobX Concepts

Four fundamental building blocks for reactive state management

Observable State

Mark data properties as observable so MobX can track changes and notify dependent code automatically.

Computed Values

Define derived state that automatically updates when underlying observables change, with intelligent caching.

Actions

Wrap state mutations in actions for explicit control, batching, and strict mode compatibility.

Reactions

Automatically execute side effects when observable data changes, including React component updates.

Core MobX Concepts

MobX introduces four fundamental concepts that work together to create a reactive state management system: observable state, computed values, actions, and reactions. Mastering these concepts gives you a complete toolkit for managing application state at any scale.

Observable State

Observable state forms the foundation of any MobX-powered application. An observable is any piece of state that MobX should track for changes. When you make data observable, MobX sets up internal tracking mechanisms that record which computed values, reactions, and components depend on that data.

import { makeAutoObservable } from 'mobx';

class TodoStore {
 todos = [];
 pendingRequests = 0;

 constructor() {
 makeAutoObservable(this);
 }

 addTodo(task) {
 this.todos.push({ task, completed: false });
 }
}

Computed Values

Computed values represent derived state--information that can be calculated from your observable state. The power of computed values lies in their automatic caching: MobX only recalculates when dependencies change.

get completedTodosCount() {
 return this.todos.filter(todo => todo.completed).length;
}

get completionPercentage() {
 if (this.todos.length === 0) return 0;
 return (this.completedTodosCount / this.todos.length) * 100;
}

According to the official MobX documentation, computed values automatically track their dependencies and only recalculate when needed, providing significant performance benefits for complex derived state.

Actions

Actions are functions that modify observable state. While you can technically change observable state from anywhere in your code, wrapping state mutations in actions provides several benefits: explicit change locations, batching support, and strict mode compatibility.

import { makeAutoObservable, runInAction } from 'mobx';

class TodoStore {
 todos = [];

 constructor() {
 makeAutoObservable(this);
 }

 addTodo(task) {
 const newTodo = { task, completed: false, assignee: null };
 this.todos.push(newTodo);
 }

 async loadTodosFromServer() {
 const data = await fetch('/api/todos').then(r => r.json());
 runInAction(() => {
 this.todos = data;
 });
 }
}

Reactions and Autorun

Reactions are the side-effect counterpart to computed values. The most common reaction is autorun, which runs a function immediately and then re-runs it whenever any observable data it accesses changes.

import { autorun } from 'mobx';

const store = new TodoStore();

// This will log the report whenever todos change
autorun(() => {
 console.log(store.report);
});

Understanding when to use actions versus computed values is important. Actions change state; computed values read and derive from state. This clear separation of concerns makes your code more predictable and easier to test.

For a comprehensive comparison of state management approaches in 2025, see the DEV Community analysis of modern React state management.

Using MobX with React

Integrating MobX with React requires the mobx-react-lite package. The key integration point is the observer function, which wraps your React components and automatically subscribes them to MobX observables. When any observable that the component accesses changes, the component re-renders with the new data.

The observer wrapper handles all subscription management automatically. Without it, React components would render once with initial data but never update when state changes. With observer, components become truly reactive--they re-render automatically and efficiently whenever their observable dependencies change.

import { observer } from 'mobx-react-lite';

const TodoList = observer(({ store }) => {
 const onNewTodo = () => {
 const task = prompt('Enter a new todo:');
 if (task) store.addTodo(task);
 };

 return (
 <div>
 <h2>Todos ({store.completedTodosCount}/{store.todos.length})</h2>
 <ul>
 {store.todos.map((todo, index) => (
 <TodoView key={index} todo={todo} />
 ))}
 </ul>
 <button onClick={onNewTodo}>Add Todo</button>
 </div>
 );
});

Notice how simple each component is. They read directly from the store without any useState, useEffect, or manual subscription management. Fine-grained reactivity is a key advantage--only components that actually use changed data re-render, optimizing performance automatically.

This pattern complements other React component patterns like compound components, where shared state needs to be coordinated across multiple related components.

Provider and Hook Pattern

For larger applications, combine MobX with React's context system to make stores available throughout your component tree. The pattern involves creating store instances, wrapping your application with a Provider component, and using custom hooks to access stores.

import { createContext, useContext } from 'react';
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';

class AppStore {
 user = null;
 preferences = { theme: 'light' };

 constructor() {
 makeAutoObservable(this);
 }

 setUser(user) {
 this.user = user;
 }
}

const AppStoreContext = createContext(null);

export function AppStoreProvider({ children }) {
 const store = new AppStore();
 return (
 <AppStoreContext.Provider value={store}>
 {children}
 </AppStoreContext.Provider>
 );
}

export function useAppStore() {
 const store = useContext(AppStoreContext);
 if (!store) {
 throw new Error('useAppStore must be used within AppStoreProvider');
 }
 return store;
}

This pattern scales well because stores are independent units that can be tested, reused, and composed together. Each store encapsulates its own domain logic while collaborating with other stores when needed. For testing strategies that work well with this architecture, see our guide on testing Next.js applications.

MobX vs Redux: A Practical Comparison

Understanding how MobX differs from Redux helps you choose the right tool for your project. Both solve state management problems but take fundamentally different approaches.

Redux's strengths:

  • Predictable state changes through actions and reducers
  • Time-travel debugging for understanding complex bugs
  • Clear conventions that help maintain consistency across large teams

MobX's strengths:

  • Minimal boilerplate, faster prototyping
  • Code that more closely resembles plain JavaScript
  • Direct mutation model more intuitive for many developers
// Redux: Action types, action creators, reducers
const ADD_TODO = 'ADD_TODO';
function addTodoAction(text) {
 return { type: ADD_TODO, payload: text };
}
function todoReducer(state = [], action) {
 switch (action.type) {
 case ADD_TODO:
 return [...state, { text: action.payload, completed: false }];
 default:
 return state;
 }
}

// MobX: Simple class with observable state
class TodoStore {
 todos = [];
 addTodo(text) {
 this.todos.push({ text, completed: false });
 }
}

Neither approach is universally better--the right choice depends on your team's preferences, project requirements, and scale. As noted in the DEV Community comparison, MobX is highlighted for its minimal boilerplate and intuitive reactive programming model, making it ideal for small to medium projects that need reactive updates without Redux-style boilerplate.

Best Practices for MobX in Production

Organizing Stores

For applications beyond trivial complexity, organize stores thoughtfully. A common approach is domain-driven design, where each major feature area has its own store class. Related stores can reference each other, allowing you to build sophisticated state graphs.

// stores/auth.js
export class AuthStore {
 user = null;
 permissions = [];

 constructor(userStore) {
 makeAutoObservable(this);
 this.userStore = userStore;
 }
}

// stores/index.js - compose stores together
export function createStores() {
 const userStore = new UserStore();
 const authStore = new AuthStore(userStore);
 return { userStore, authStore };
}

Performance Considerations

  • Always wrap components using observable data in observer
  • Be mindful of computed value dependencies with large collections
  • Consider restructuring data for expensive operations

Server-Side Rendering with Next.js

Using MobX with Next.js requires creating fresh store instances for each request and managing hydration carefully:

class AppStore {
 user = null;
 constructor() {
 makeAutoObservable(this);
 }
 async initialize() {
 this.user = await fetchUser();
 }
}

// Per-request initialization
const store = new AppStore();
await store.initialize();

This pattern ensures stores are properly isolated per request while maintaining MobX's reactivity benefits. For more on testing strategies with Next.js, including how to test MobX stores, see our guide on comparing Next.js testing tools.

When to Use MobX

MobX excels in several common scenarios:

Ideal use cases:

  • Applications with complex, interconnected state
  • Dashboards displaying real-time data
  • Wizards with multiple dependent steps
  • Collaborative tools with shared state
  • Projects prioritizing developer velocity

Consider alternatives when:

  • Very large applications with many contributors benefit from Redux's explicit conventions
  • Complex debugging requirements where tracking every state change matters
  • Teams deeply invested in functional programming paradigms

For modern React development with Next.js, MobX integrates cleanly without requiring architectural changes. Understanding MobX gives you a powerful tool that balances simplicity with the capabilities complex applications demand.

Whether you're building a simple todo list or a complex enterprise application, these patterns scale naturally. Combined with thoughtful store organization and attention to performance, MobX can serve as the state management backbone for projects of any size. Our team specializes in web development and can help you architect applications that leverage MobX effectively for your specific needs.

Frequently Asked Questions

Is MobX better than Redux?

Neither is universally better. MobX offers less boilerplate and a more intuitive API for many developers, while Redux provides explicit conventions and debugging tools that scale well for very large teams. Choose based on your team's preferences and project requirements.

Does MobX work with React hooks?

Yes, MobX works perfectly with functional components and hooks. The mobx-react-lite package provides the observer wrapper and integrates naturally with React's hook-based patterns.

Can I use MobX with TypeScript?

MobX has excellent TypeScript support with full type inference. The makeAutoObservable and makeObservable functions understand types automatically, and computed values and actions are fully typed.

Is MobX still maintained in 2025?

MobX remains actively maintained with regular updates. It's battle-tested in production applications and continues to evolve with modern React patterns including concurrent features.

Ready to Build Scalable React Applications?

Our team specializes in modern React development with clean state management patterns. Let us help you architect applications that scale.

Sources

  1. MobX: Ten minute introduction to MobX and React - Official documentation covering core concepts, observables, computed values, actions, and React integration.

  2. Modern React State Management in 2025: A Practical Guide - Comprehensive comparison of state management libraries including Redux Toolkit, MobX, Recoil, Zustand, and Jotai.