How MobX Manages Shared State Across Plane's Frontend Applications

Plane implements a centralized state management system using MobX observable stores composed into a single CoreRootStore and distributed via React Context to synchronize data across its web and Community Edition (CE) applications.

Plane's frontend architecture relies on MobX to maintain a single source of truth for application state. The open-source project management platform uses a store-centric pattern where TypeScript classes define observable properties that automatically trigger UI updates. This approach enables seamless state sharing between the main web application and the CE frontend while maintaining type safety and SSR compatibility.

Observable Store Classes with makeObservable

Individual feature stores in Plane are implemented as plain TypeScript classes that declare their state using MobX's makeObservable utility. In packages/shared-state/src/store/workspace.store.ts, the WorkspaceStore defines observable properties for workspace metadata:

import { makeObservable, observable } from "mobx";

export class WorkspaceStore implements IWorkspaceStore {
  id: string;
  name: string;
  createdAt: string;
  updatedAt: string;

  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;
    this.createdAt = data.createdAt;
    this.updatedAt = data.updatedAt;
  }
}

This pattern ensures that any changes to these properties automatically notify subscribed components.

Composing State with CoreRootStore

Application-wide state coordination happens in apps/web/core/store/root.store.ts, where the CoreRootStore aggregates all feature-specific stores into a unified hierarchy. This root store instantiates children like WorkspaceRootStore, ProjectRootStore, and IssueRootStore, passing its own reference to enable cross-store communication:

import { enableStaticRendering } from "mobx-react";

enableStaticRendering(typeof window === "undefined");

export class CoreRootStore {
  workspaceRoot: WorkspaceRootStore;
  projectRoot:   ProjectRootStore;
  issue:         IssueRootStore;

  constructor() {
    this.workspaceRoot = new WorkspaceRootStore(this as unknown as RootStore);
    this.projectRoot   = new ProjectRootStore(this);
    this.issue         = new IssueRootStore(this as unknown as RootStore);
  }
}

The implementation also addresses server-side rendering concerns by calling enableStaticRendering at the module level, which disables MobX's reactive tracking during SSR to prevent memory leaks.

React Integration via Context

To make the state tree accessible throughout the component hierarchy, Plane uses a React Context pattern defined in apps/web/core/lib/store-context.tsx. The StoreProvider component wraps the application and injects a singleton RootStore instance:

import { createContext } from "react";

export let rootStore = new RootStore();
export const StoreContext = createContext<RootStore>(rootStore);

export function StoreProvider({ children }: { children: ReactElement }) {
  return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
}

Components consume this context using useContext and wrap themselves with the observer HOC from mobx-react-lite. This subscription mechanism ensures components re-render only when the specific observables they access actually change:

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

const WorkspaceName = observer(() => {
  const { workspaceRoot } = useContext(StoreContext);
  return <h1>{workspaceRoot.workspace.name}</h1>;
});

Cross-Application State Sharing

Both the web (apps/web) and Community Edition (apps/ce) frontends import store definitions from the shared @plane/shared-state package. Because stores are framework-agnostic TypeScript classes, the same business logic and state structures power multiple application bundles without duplication.

The StoreProvider ensures each navigation session maintains a single source of truth, while the shared package guarantees type consistency across deployment targets.

State Reset and Lifecycle Management

When users sign out, Plane clears sensitive state to prevent data leakage between sessions. The resetOnSignOut method in CoreRootStore (found at line 137 of apps/web/core/store/root.store.ts) reinstantiates each child store, returning the application to its initial clean state.

Summary

  • Plane uses MobX observable classes defined in packages/shared-state to model application state with makeObservable
  • The CoreRootStore in apps/web/core/store/root.store.ts composes all feature stores into a single hierarchy
  • enableStaticRendering ensures SSR compatibility by disabling reactive tracking on the server
  • React Context (StoreContext) distributes the root store globally via StoreProvider
  • Components use observer from mobx-react-lite for fine-grained, automatic re-rendering
  • The @plane/shared-state package enables code sharing between web and CE applications
  • resetOnSignOut provides secure state cleanup between user sessions

Frequently Asked Questions

How does Plane prevent memory leaks during server-side rendering with MobX?

Plane calls enableStaticRendering from mobx-react at the top of apps/web/core/store/root.store.ts to disable MobX's reaction tracking when running on the server. This check against typeof window === "undefined" ensures that server-side renders don't accumulate active reactions that would leak memory between requests.

What pattern does Plane use to share state between different frontend applications?

Plane packages its store definitions in @plane/shared-state and imports them into both the web and CE applications. A singleton CoreRootStore instantiated in apps/web/core/lib/store-context.tsx provides a single source of truth per session, while React Context makes this store accessible to any component in the tree regardless of which application bundle is running.

How do components subscribe to MobX state changes in Plane?

Components import the observer function from mobx-react-lite and wrap their component definitions. Inside the component, they access the store via useContext(StoreContext). MobX automatically tracks which observable properties are accessed during render, and the observer HOC ensures the component re-renders only when those specific properties change.

Where does Plane handle state cleanup when a user signs out?

The CoreRootStore class implements a resetOnSignOut method that reinstantiates all child stores including workspace, project, and user stores. This method, located at line 137 of apps/web/core/store/root.store.ts, ensures that stale user data is completely removed from memory before the next authentication session begins.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →