# MobX Store Architecture for Global State Management in Plane

> Explore the MobX store architecture for global state management in Plane. Learn how CoreRootStore aggregates feature stores via StoreContext for efficient frontend state handling.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: architecture
- Published: 2026-06-22

---

**Plane implements a single-root MobX store pattern where a centralized `CoreRootStore` aggregates dozens of feature-specific stores and exposes them to React components via a dedicated `StoreContext`.**

The frontend of Plane (makeplane/plane) relies on a hierarchical MobX store architecture to handle complex global state management across its project management interface. This architecture centers on a single root store that instantiates and wires together domain-specific stores for workspaces, issues, projects, and UI themes. By combining MobX's reactive observables with React's context API, Plane delivers fine-grained state updates while keeping component code declarative and type-safe.

## Core Components of the Store Architecture

Plane’s MobX store architecture consists of three primary layers: the root store that aggregates all state, the React context that distributes it, and the feature-specific stores that encapsulate domain logic.

### CoreRootStore and RootStore Hierarchy

The **`CoreRootStore`** class serves as the backbone of Plane’s state management system. Located in [`apps/web/core/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/root.store.ts), this class is responsible for instantiating every sub-store—including `projectRoot`, `issue`, `router`, `theme`, and user management stores—and wiring them together at application startup.

When an application requires additional stores beyond the core set, Plane uses an extension pattern. The Community Edition (CE) app extends `CoreRootStore` in [`apps/web/ce/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/ce/store/root.store.ts), adding only the stores unique to that deployment while inheriting the full core functionality. This hierarchy ensures consistent state management across different Plane deployments while allowing for customization.

### StoreContext and StoreProvider

State distribution relies on **`StoreContext`**, a React context created via `React.createContext<RootStore | undefined>(undefined)` and defined in [`apps/web/lib/store-context.ts`](https://github.com/makeplane/plane/blob/main/apps/web/lib/store-context.ts). The **`StoreProvider`** component—typically placed in [`apps/web/pages/_app.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/pages/_app.tsx)—instantiates the root store once and supplies it to the component tree:

```tsx
import { StoreContext } from '@/lib/store-context';
import { RootStore } from '@/store/root.store';

function MyApp({ Component, pageProps }) {
  const rootStore = useRef(new RootStore()).current;

  return (
    <StoreContext.Provider value={rootStore}>
      <Component {...pageProps} />
    </StoreContext.Provider>
  );
}

```

### Feature-Specific Sub-Stores

Each domain area manages its own state through dedicated stores instantiated by the root store. For example, `WorkspaceStore` in [`packages/shared-state/src/store/workspace.store.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/store/workspace.store.ts) handles workspace data, while `ProjectRootStore` and `IssueRootStore` manage project and issue state respectively. These stores receive a reference to the root store during construction, enabling cross-store communication when needed.

## Store Initialization Flow

The creation and wiring of Plane’s MobX store architecture follows a strict initialization sequence:

1. **Application startup** creates a new `RootStore()` instance (or `CoreRootStore()` for admin/space apps).
2. The `RootStore` constructor calls `super()` to invoke `CoreRootStore`’s constructor, which instantiates the **router** (`new RouterStore()`), **command palette**, **instance**, **user**, **theme**, and every domain store (`new ProjectRootStore(this)`, `new IssueRootStore(this)`, etc.).
3. The constructor passes the root store (`this`) to sub-stores that require cross-store access, such as `UserStore(this as unknown as RootStore)`.
4. The instantiated root store is placed in React context via `<StoreContext.Provider value={rootStore}>`.
5. **React components** consume the context through specialized hooks like `useWorkspace()` or `useProject()`, each calling `useContext(StoreContext)` and returning the specific sub-store required.

## Accessing Stores in React Components

Components access global state through thin wrapper hooks rather than interacting with context directly. The `useWorkspace` hook in [`apps/web/core/hooks/store/use-workspace.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/hooks/store/use-workspace.ts) demonstrates this pattern:

```tsx
import { useWorkspace } from '@/hooks/store/use-workspace';

function WorkspaceHeader() {
  const workspace = useWorkspace();   // returns IWorkspaceRootStore
  return <h1>{workspace.currentWorkspace?.name ?? 'Loading…'}</h1>;
}

```

This approach provides type-safe access to specific store slices while keeping components decoupled from the store architecture's internal structure.

## State Management Patterns

Plane leverages MobX’s `makeObservable` utility to define reactive properties. In [`packages/shared-state/src/store/workspace.store.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/store/workspace.store.ts), observable references are configured explicitly:

```ts
export class WorkspaceStore implements IWorkspaceStore {
  constructor(data: IWorkspaceStore) {
    makeObservable(this, {
      id:   observable.ref,
      name: observable.ref,
      createdAt: observable.ref,
      updatedAt: observable.ref,
    });
    this.id = data.id;
    this.name = data.name;
    // ...
  }
}

```

State mutations occur through actions that batch notifications. The `fetchProjectStates` method in [`apps/web/core/store/state.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/state.store.ts) illustrates the pattern for async data fetching:

```ts
@action
async fetchProjectStates(workspaceSlug: string, projectId: string) {
  const response = await this.stateService.getStates(workspaceSlug, projectId);
  runInAction(() => {
    response.forEach(state => set(this.stateMap, [state.id], state));
    this.fetchedMap[projectId] = true;
  });
}

```

Because the method is decorated with `@action`, MobX batches change notifications, ensuring that components observing `stateMap` or `fetchedMap` re-render efficiently and predictably.

## Summary

- Plane uses a **single-root MobX store** (`CoreRootStore`) that aggregates all feature-specific stores into one hierarchical object.
- The root store is instantiated once at startup and provided to React via **`StoreContext`** in [`apps/web/lib/store-context.ts`](https://github.com/makeplane/plane/blob/main/apps/web/lib/store-context.ts).
- **Domain stores** (workspace, issue, project) receive the root store reference during construction, enabling cross-store communication.
- Components access state through **thin wrapper hooks** (e.g., `useWorkspace`, `useProject`) that read from `StoreContext`.
- State updates use MobX **`@action`** decorators and **`makeObservable`** to ensure fine-grained reactivity and predictable update batches.

## Frequently Asked Questions

### What is the root store in Plane's MobX architecture?

The root store is the `CoreRootStore` class defined in [`apps/web/core/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/root.store.ts). It acts as a container that instantiates and holds references to all feature-specific stores such as `WorkspaceRootStore`, `IssueRootStore`, and `RouterStore`. This single root object is passed through React context to make global state accessible throughout the component tree.

### How do React components access MobX stores in Plane?

Components access stores through specialized hooks rather than direct context consumption. Hooks like `useWorkspace()` (found in [`apps/web/core/hooks/store/use-workspace.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/hooks/store/use-workspace.ts)) call `useContext(StoreContext)` internally and return the specific sub-store requested. This pattern provides type-safe access while keeping components decoupled from the store hierarchy's implementation details.

### What pattern does Plane use for store initialization?

Plane follows a hierarchical constructor pattern where `RootStore` extends `CoreRootStore`. During instantiation, the constructor creates all sub-stores and passes `this` (the root store instance) to any sub-store requiring cross-store references. This occurs once at application startup in the `StoreProvider` component, typically located in [`apps/web/pages/_app.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/pages/_app.tsx).

### How does Plane handle state updates with MobX?

State updates occur through class methods decorated with MobX's `@action` decorator, which ensures that all state changes within the method are batched into a single notification. For asynchronous operations, Plane uses `runInAction` to wrap state mutations after async calls complete. Observables are defined using `makeObservable` with `observable.ref` for efficient reference tracking, ensuring components only re-render when specific properties they observe actually change.