# How State Is Managed in the Plane Frontend Applications

> Discover how Plane frontend applications manage state with a centralized MobX store architecture, leveraging React context and the observer pattern for reactive UI. Learn about SSR support.

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

---

**Plane’s frontend applications use a centralized MobX store architecture where a RootStore composes feature-specific stores, exposes them via React context, and drives UI reactivity through the `observer` pattern, with server-side rendering support via `enableStaticRefreshing`.**

The open-source project management platform [makeplane/plane](https://github.com/makeplane/plane) employs a sophisticated state management strategy across its web-based frontends. The main web app, Space app, and Admin app all rely on a hierarchical MobX store pattern that ensures type-safe, reactive state sharing while maintaining clean separation between server and client environments.

## The Root Store Architecture

Each Plane application instantiates a central `RootStore` (or `CoreRootStore`) class that serves as the single source of truth. This root store composes multiple feature-specific sub-stores such as `UserStore`, `ProjectStore`, `IssueStore`, and `CycleStore`, wiring them together in its constructor to handle cross-cutting concerns.

The architecture emphasizes explicit lifecycle management. Each root store implements `reset` or `resetOnSignOut` methods that recreate the entire store graph when a user logs out, ensuring no stale data persists between sessions.

- **Web app root store:** [`apps/web/core/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/root.store.ts)
- **Space app root store:** [`apps/space/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/space/store/root.store.ts)

These files instantiate sub-stores and expose them as public properties, allowing any component that accesses the root store to reach nested state containers.

## MobX Integration and React Context

The stores leverage MobX's `makeAutoObservable` to automatically track state mutations and compute derived values. To support Next.js server-side rendering, Plane invokes `enableStaticRendering(true)` from `mobx-react` at the top of each root-store file, preventing memory leaks by ensuring observers do not persist across server requests.

The instantiated `RootStore` propagates through the component tree via a dedicated React context. The `StoreContext` is created in [`apps/web/core/lib/store-context.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/core/lib/store-context.tsx) and supplied by a `StoreProvider` component that wraps the entire application:

```tsx
import { StoreProvider } from "@/store-context";

function App() {
  return (
    <StoreProvider>
      <YourRoutesOrComponents />
    </StoreProvider>
  );
}

```

This pattern ensures that all stores are singletons within the application scope while remaining testable and resettable.

## Accessing State in UI Components

Components consume the store via `useContext(StoreContext)` and become reactive by wrapping with the `observer` higher-order component from `mobx-react`. Because MobX tracks property access at runtime, any observable mutation in a sub-store triggers a targeted re-render only in components that depend on that specific data.

For example, a component reading project data would access the store and destructure observables like this:

```tsx
import { useContext } from "react";
import { observer } from "mobx-react";
import { StoreContext } from "@/store-context";

const ProjectList = observer(() => {
  const { projectRoot } = useContext(StoreContext);
  const { projects } = projectRoot;               // `projects` is an observable array

  return (
    <ul>
      {projects.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
});

```

Concrete implementation patterns appear in navigation components, such as [`apps/web/core/components/navigation/use-tab-preferences.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/components/navigation/use-tab-preferences.ts), which demonstrates reading and writing MobX state within hooks.

## Shared State Across Applications

Plane extracts common state logic into the `@plane/shared-state` package to avoid duplication between the web and Space applications. This package exports dedicated stores like `WorkItemFilterStore` and `RichFiltersStore` from [`packages/shared-state/src/store/index.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/store/index.ts).

These shared stores follow the same MobX patterns but reside in a separate package, allowing consistent filter logic across different frontend entry points:

```ts
import { WorkItemFilterStore } from "@plane/shared-state";

const filterStore = new WorkItemFilterStore();

filterStore.setStatus("open");
filterStore.setAssignee(userId);
// UI components observing `filterStore` update automatically

```

The specific implementation of work-item filtering resides in [`packages/shared-state/src/store/work-item-filters/filter.store.ts`](https://github.com/makeplane/plane/blob/main/packages/shared-state/src/store/work-item-filters/filter.store.ts), which defines observable properties for status, assignees, and other filter criteria.

## State Lifecycle: Hydration and Reset

After the server renders the initial HTML, the client hydrates MobX stores with API data through `hydrate` methods defined in individual stores. This transforms server-fetched JSON into observable class instances.

When a user signs out, the `resetOnSignOut` method on the root store clears all observables and recreates fresh store instances:

```ts
import { store } from "@/store-context";

export function signOut() {
  // …perform API logout…
  store.resetOnSignOut();   // clears every observable and recreates fresh instances
}

```

This ensures sensitive project data is purged from memory and the UI returns to a pristine state without requiring a full page reload.

## Summary

- **Centralized Root Store:** Each app uses a `RootStore` class that instantiates and wires together feature-specific stores like `ProjectStore` and `IssueStore`.
- **MobX Reactivity:** State mutations trigger automatic UI updates via `makeAutoObservable` and the `observer` HOC, with `enableStaticRendering` ensuring SSR safety.
- **Context Provider Pattern:** The `StoreContext` in [`apps/web/core/lib/store-context.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/core/lib/store-context.tsx) makes the root store accessible throughout the component tree.
- **Cross-App Reusability:** The `@plane/shared-state` package exports stores for common concerns like work-item filters, imported by multiple frontend applications.
- **Explicit Lifecycle:** `hydrate` methods seed stores with server data, while `resetOnSignOut` guarantees a clean slate on authentication changes.

## Frequently Asked Questions

### How does Plane handle server-side rendering with MobX?

Plane calls `enableStaticRendering(true)` from `mobx-react` at the initialization of each root store file. According to the makeplane/plane source code, this prevents MobX from keeping observers alive during server-side rendering, avoiding memory leaks and ensuring that each HTTP request receives a fresh, isolated store instance.

### What is the difference between the web app and Space app store implementations?

Both applications follow the identical architectural pattern of a `RootStore` composing sub-stores and exposing them via `StoreContext`. However, they reside in separate directories—[`apps/web/core/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/root.store.ts) versus [`apps/space/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/space/store/root.store.ts)—and instantiate different subsets of stores based on their specific feature requirements, while sharing common logic via the `@plane/shared-state` package.

### How do components access and react to state changes?

Components retrieve the store instance via `useContext(StoreContext)` and wrap their export with the `observer` function from `mobx-react`. As implemented in components like those using [`use-tab-preferences.ts`](https://github.com/makeplane/plane/blob/main/use-tab-preferences.ts), this combination ensures that the component re-renders automatically whenever any observable property it accesses is mutated, without requiring manual subscription management.

### How is state reset when a user signs out?

The root store exposes a `resetOnSignOut` method—defined in [`apps/web/core/store/root.store.ts`](https://github.com/makeplane/plane/blob/main/apps/web/core/store/root.store.ts)—that recreates all sub-store instances and clears observable state. When the authentication layer calls this method during logout, it guarantees that no cached project data or user preferences persist in memory for the next session.