# How the Frontend is Developed in the Plane Project: React, MobX, and Monorepo Architecture

> Explore the Plane frontend development with React 18, MobX state management, and Turborepo monorepo architecture. Learn about routing, UI components, and Tailwind CSS.

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

---

**The Plane frontend is a client-side React 18 application built within a Turborepo monorepo, utilizing React Router 6 for file-based routing, MobX for state management, and a custom UI component library styled with Tailwind CSS.**

The Plane project is an open-source project management platform that organizes its frontend as a modular, type-safe React application residing in the `apps/web` workspace. According to the Plane source code, the architecture emphasizes developer experience through automated route generation, centralized state management, and a comprehensive design system housed in internal packages.

## Architecture and Technology Stack

The frontend architecture follows a modern React pattern with clear separation of concerns across multiple internal packages.

### Framework and Routing

The application runs as a **client-side single-page application (SPA)** using **React 18** with TypeScript. In [`apps/web/react-router.config.ts`](https://github.com/makeplane/plane/blob/main/apps/web/react-router.config.ts), the configuration explicitly disables server-side rendering (`ssr: false`), directing React Router to generate a static client bundle.

Routing follows a **file-based convention** where the folder structure automatically maps to URLs. Route files live under `apps/web/app/(…)/` directories, with [`page.tsx`](https://github.com/makeplane/plane/blob/main/page.tsx) files defining route components. For example, `app/(all)/workspace-views/page.tsx` automatically registers as the `/workspace-views` route.

### State Management

Global application state is handled by **MobX** (`mobx`, `mobx-react`), exposed through the `@plane/shared-state` package. This centralized store pattern manages workspace data, user preferences, and real-time synchronization across components.

### UI Library and Design System

Reusable UI components reside in the **`packages/ui`** package (published as `@plane/ui`). Key components include:

- **Button** ([`packages/ui/src/button/button.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/button/button.tsx)): Typed, accessible button variants
- **Spinners** ([`packages/ui/src/spinners/circular-spinner.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/spinners/circular-spinner.tsx)): Loading state indicators
- **Modals and Tables**: Complex layout primitives

The design system uses **Tailwind CSS** with custom utility classes defined in `@plane/tailwind-config`, ensuring consistent theming across the monorepo.

### Data Fetching and API Integration

Data synchronization uses **`swr`** for caching and revalidation, paired with **`@plane/services`**—a thin abstraction layer over the backend REST and GraphQL endpoints. This combination provides automatic caching, optimistic updates, and error retry logic.

Internationalization is handled by `@plane/i18n`, which loads locale JSON files at runtime for multi-language support.

## Project Structure and Key Files

The Plane frontend operates within apnpm workspace monorepo structure, using `workspace:*` syntax for internal dependencies and `catalog:` aliases for external packages.

### Core Configuration Files

| File | Purpose |
|------|---------|
| [`apps/web/package.json`](https://github.com/makeplane/plane/blob/main/apps/web/package.json) | Declares dependencies, scripts, and build configuration |
| [`apps/web/react-router.config.ts`](https://github.com/makeplane/plane/blob/main/apps/web/react-router.config.ts) | Configures React Router for client-side SPA mode |
| [`apps/web/app/root.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/app/root.tsx) | Root component mounting the app rail, progress bar, and outlet |
| [`apps/web/app/provider.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/app/provider.tsx) | Provides MobX stores, theme context, and i18n to the application tree |
| `apps/web/app/(all)/layout.tsx` | Layout wrapper applied to all routes (header, sidebar, navigation) |

### Navigation and Progress Indicators

The **`AppProgressBar`** component ([`apps/web/core/lib/b-progress/AppProgressBar.tsx`](https://github.com/makeplane/plane/blob/main/apps/web/core/lib/b-progress/AppProgressBar.tsx)) integrates with React Router's `useNavigation` hook to display a thin top-loading bar during route transitions, providing visual feedback for asynchronous navigation states.

## Development Patterns and Code Examples

### Creating Routes with React Router

New pages are created by adding files to the `app` directory following the convention `(route-group)/route-name/page.tsx`:

```typescript
// apps/web/app/(all)/example/page.tsx
import { useEffect } from "react";
import { useParams } from "react-router";

export default function ExamplePage() {
  const { exampleId } = useParams();

  useEffect(() => {
    console.log("Loaded example:", exampleId);
  }, [exampleId]);

  return (
    <section className="p-4">
      <h1 className="text-2xl font-bold">Example #{exampleId}</h1>
      <p className="mt-2">This page automatically maps to /example/:exampleId</p>
    </section>
  );
}

```

When placed at `app/(all)/example/page.tsx`, React Router automatically registers this as `/example/:exampleId` without manual route configuration.

### Consuming UI Components

Components from the internal design system are imported from `@plane/ui`:

```typescript
import { Button } from "@plane/ui";

export function SaveButton({ onSave }: { onSave: () => void }) {
  return (
    <Button
      variant="primary"
      className="rounded-md px-4 py-2"
      onClick={onSave}
    >
      Save Changes
    </Button>
  );
}

```

The Button component in [`packages/ui/src/button/button.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/button/button.tsx) provides full TypeScript support and Tailwind styling out of the box.

### Data Fetching with SWR

The recommended pattern for API interaction combines SWR with the internal services layer:

```typescript
import useSWR from "swr";
import { getWorkspace } from "@plane/services";

export function WorkspaceName({ workspaceId }: { workspaceId: string }) {
  const { data, error } = useSWR(
    ["workspace", workspaceId],
    () => getWorkspace(workspaceId)
  );

  if (error) return <span className="text-red-500">Failed to load</span>;
  if (!data) return <span className="opacity-50">Loading workspace…</span>;

  return <span>{data.name}</span>;
}

```

The `@plane/services` package encapsulates endpoint logic, while SWR handles caching, deduplication, and background revalidation.

## Build and Development Workflow

Development runs via **`pnpm dev`** (configured as `react-router dev --port 3000`), with Vite handling hot module replacement and bundling. Production builds generate static assets via `react-router build`, producing deployable bundles in the `plane-frontend` Docker image.

Testing coverage includes **Storybook** for UI component visualization (`pnpm --filter=@plane/ui storybook`) and **Vitest** for unit testing across packages. Continuous integration pipelines in [`.github/workflows/pull-request-build-lint-web-apps.yml`](https://github.com/makeplane/plane/blob/main/.github/workflows/pull-request-build-lint-web-apps.yml) enforce build validation and linting standards.

## Summary

- **Architecture**: Client-side React 18 SPA with React Router 6 file-based routing, configured in [`apps/web/react-router.config.ts`](https://github.com/makeplane/plane/blob/main/apps/web/react-router.config.ts) with `ssr: false`
- **State**: MobX stores exposed through `@plane/shared-state` for predictable global state management
- **Styling**: Tailwind CSS with custom configuration in `@plane/tailwind-config` and components in `@plane/ui`
- **Data**: SWR for caching and revalidation, backed by `@plane/services` for API abstraction
- **Structure**: Monorepo workspace with internal packages using `workspace:*` references
- **Tooling**: Vite bundler, TypeScript throughout, Storybook for UI development, Docker for deployment

## Frequently Asked Questions

### What routing system does Plane use for its frontend?

Plane uses **React Router 6** with a file-based routing convention. Routes are defined by creating [`page.tsx`](https://github.com/makeplane/plane/blob/main/page.tsx) files within the `apps/web/app/` directory structure, where folder names map directly to URL paths. The configuration in [`react-router.config.ts`](https://github.com/makeplane/plane/blob/main/react-router.config.ts) explicitly sets `ssr: false`, ensuring a static client-side bundle optimized for SPA behavior.

### How does Plane handle global state management?

The frontend uses **MobX** (`mobx` and `mobx-react`) for global state management, wrapped in the `@plane/shared-state` internal package. State is provided to the component tree through [`app/provider.tsx`](https://github.com/makeplane/plane/blob/main/app/provider.tsx), which initializes stores for workspace data, user sessions, and real-time synchronization, making them accessible throughout the application without prop drilling.

### Where are the reusable UI components located in the Plane codebase?

Reusable UI components reside in **`packages/ui`** (imported as `@plane/ui`). This package contains typed React components such as buttons ([`packages/ui/src/button/button.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/button/button.tsx)), spinners ([`packages/ui/src/spinners/circular-spinner.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/spinners/circular-spinner.tsx)), modals, and tables. All components use Tailwind CSS for styling and are documented and tested via Storybook.

### How does the frontend communicate with the Plane backend?

Data fetching uses **`swr`** combined with the internal **`@plane/services`** package. SWR provides caching, revalidation, and optimistic update patterns, while `@plane/services` abstracts the underlying REST and GraphQL endpoints. This pattern is implemented in `packages/hooks` with custom React hooks for specific data domains, ensuring consistent error handling and loading states across the application.