# What Is the Web Directory in makeplane/plane? A Complete Guide to the Plane Frontend

> Explore the web directory in makeplane/plane, the client-side React app handling routing, state, API calls, and UI. Understand the Plane frontend with this complete guide.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: deep-dive
- Published: 2026-06-25

---

**The `apps/web` directory contains the entire client-side React application for Plane, serving as the browser-based interface that handles routing, state management, API communication, and UI rendering.**

The Plane repository is structured as a monorepo where the `web` directory lives under `apps/web`. This folder encapsulates the single-page application (SPA) that users interact with in their browsers, built with modern React tooling and optimized for performance as a static client bundle.

## Architecture and Entry Points

The Plane web application follows a convention-based routing architecture centered around React Router v7, configured explicitly for client-side rendering only.

### Root Component and Routing

The entry point for the application is defined in [`app/root.tsx`](https://github.com/makeplane/plane/blob/main/app/root.tsx). This file establishes the HTML skeleton, injects global meta tags, preloads custom fonts via `<link rel="preload">`, and mounts the theme provider from `next-themes`. It serves as the mounting point for the React Router tree.

Routing configuration is split between [`react-router.config.ts`](https://github.com/makeplane/plane/blob/main/react-router.config.ts) and the `app/routes/` directory. Critically, the configuration explicitly disables server-side rendering:

```typescript
// apps/web/react-router.config.ts
export default {
  // Server-side rendering is disabled - this is a client-only SPA
  ssr: false,
  // Routes are defined in the app/routes directory
} satisfies Config;

```

The route tree in `app/routes/**` defines all navigable pages, layouts, and error boundaries. During development, the application runs on `http://localhost:3000`, serving the fully client-rendered interface.

### Build System Configuration

The build pipeline is managed by Vite, configured in [`vite.config.ts`](https://github.com/makeplane/plane/blob/main/vite.config.ts). This setup processes the React Router dev plugin and handles environment variable filtering. Only client assets are emitted, reinforcing the SPA architecture:

```typescript
// apps/web/vite.config.ts
export default defineConfig({
  plugins: [
    reactRouterDevPlugin(),
    // ... other plugins
  ],
  // SSR explicitly disabled in build output
  build: {
    outDir: 'build/client',
  },
});

```

## State Management and Data Flow

State within the Plane web directory follows a structured pattern separating UI state from API communication.

### MobX Stores

Domain state is maintained in MobX observable stores located in `core/store/**`. These stores expose observable values, computed selectors, and async actions. For example, the project store manages workspace projects:

```typescript
// apps/web/core/store/project/project.store.ts
public async fetchProjects(workspaceSlug: string) {
  this.loader = "init-loader";
  const projects = await this.projectService.getProjects(workspaceSlug);
  runInAction(() => {
    projects.forEach(p => this.projectMap[p.id] = { ...this.projectMap[p.id], ...p });
    this.loader = "loaded";
    this.fetchStatus = "complete";
  });
  return projects;
}

```

Stores are organized by domain (projects, workspaces, webhooks) and provide the single source of truth for UI components.

### API Services

The service layer in `core/services/**` provides thin wrappers around the Django backend API. These services handle HTTP requests using `axios` and return typed DTOs defined in `@plane/types`:

```typescript
// apps/web/core/services/project.service.ts
export class ProjectService extends APIService {
  async getProjects(workspaceSlug: string): Promise<IProject[]> {
    const { data } = await this.get(`/api/workspaces/${workspaceSlug}/projects/`);
    return data;
  }
}

```

This separation ensures components interact with stores, while stores delegate persistence to services.

## UI Components and Styling

### Component Organization

While shared UI widgets reside in the separate `@plane/ui` workspace package, the `web` directory contains page-level components in `app/pages/**` and application-specific components in `app/components/**`. Components consume stores using the `observer` pattern from `mobx-react`:

```tsx
// apps/web/app/(all)/[workspaceSlug]/settings/projects/page.tsx
import { observer } from "mobx-react";
import { useRootStore } from "@/hooks/use-root-store";

const ProjectsPage = observer(() => {
  const { projectRoot } = useRootStore();
  const ids = projectRoot.workspaceProjectIds ?? [];

  return (
    <ul>
      {ids.map(id => (
        <li key={id}>{projectRoot.getProjectById(id)?.name}</li>
      ))}
    </ul>
  );
});

```

### Global Styles and Assets

Global styling is implemented via Tailwind CSS and custom theme files in `styles/*.css`, imported by the root component. Static assets including icons, favicons, and the PWA manifest reside in `public/`.

The application includes Progressive Web App support through [`public/sw.js`](https://github.com/makeplane/plane/blob/main/public/sw.js) (service worker) and [`public/site.webmanifest.json`](https://github.com/makeplane/plane/blob/main/public/site.webmanifest.json), enabling optional offline usage for installed applications.

## Development Workflow

### Package Configuration

The [`package.json`](https://github.com/makeplane/plane/blob/main/package.json) file defines the workspace dependencies and monorepo links to shared packages such as `@plane/ui`, `@plane/types`, and `@plane/shared-state`. Key scripts include:

- `dev` – Starts the Vite development server
- `build` – Creates the production client bundle
- `check:type` – Runs TypeScript type checking

### Environment Variables

Sensitive configuration is handled through environment variables prefixed with `VITE_`. Notably, when `VITE_ENABLE_SESSION_RECORDER` is set, the build injects Microsoft Clarity analytics scripts into the HTML head, filtered and validated in [`vite.config.ts`](https://github.com/makeplane/plane/blob/main/vite.config.ts).

## Summary

- The `web` directory in `makeplane/plane` is the **React SPA frontend** located at `apps/web` in the monorepo.
- It uses **Vite** for building and **React Router v7** with **SSR disabled**, producing a static client-only bundle.
- **MobX** handles state management in `core/store/`, while `core/services/` provides typed API clients for the Django backend.
- The entry point [`app/root.tsx`](https://github.com/makeplane/plane/blob/main/app/root.tsx) manages the HTML shell, theme provider, and global asset loading.
- **PWA support** is built-in via service workers and web manifests in the `public/` directory.

## Frequently Asked Questions

### What framework does the Plane web directory use?

The Plane web directory is built as a **React** single-page application using **React Router v7** for navigation and **Vite** as the build tool. It leverages **TypeScript** throughout and uses **Tailwind CSS** for styling.

### Is the Plane web app server-side rendered (SSR)?

No, SSR is explicitly disabled. The configuration in [`react-router.config.ts`](https://github.com/makeplane/plane/blob/main/react-router.config.ts) sets `ssr: false`, and [`vite.config.ts`](https://github.com/makeplane/plane/blob/main/vite.config.ts) emits only client assets. This makes Plane a fully client-rendered application where the browser downloads a static JavaScript bundle that renders the UI dynamically.

### How does the web directory communicate with the backend?

Communication occurs through the service layer in `core/services/**`. These TypeScript services use **axios** to make HTTP requests to the Django REST API, returning typed data transfer objects from `@plane/types`. MobX stores then consume these services to fetch and cache data.

### What state management solution is used in Plane's web directory?

The application uses **MobX** for reactive state management. Stores in `core/store/**` hold observable state and actions, while components use `mobx-react`'s `observer` function to reactively re-render when relevant state changes. This pattern separates UI components from data fetching logic.