Plane Project Monorepo Structure: A Deep Dive into the pnpm and Turbo Architecture

Plane uses a pnpm-managed monorepo that consolidates frontend applications, backend services, and shared TypeScript libraries under a single repository coordinated by TurboRepo.

The makeplane/plane repository organizes a complex product ecosystem—including the main web UI, admin panels, real-time collaboration servers, and reusable utility libraries—into a unified workspace. Understanding this Plane project monorepo structure helps developers navigate the codebase, run cross-application builds, and contribute new features efficiently.

Workspace Configuration and Root Setup

The monorepo's foundation is defined in pnpm-workspace.yaml at the repository root. This file instructs pnpm which directories to include in the workspace while explicitly excluding certain backend services that ship as Docker containers.


# https://github.com/makeplane/plane/blob/preview/pnpm-workspace.yaml

packages:
  - apps/*          # All UI apps (web, admin, space, live, etc.)

  - packages/*      # Shared TypeScript libraries (utils, logger, i18n, etc.)

  - "!apps/api"     # API server is built separately (Docker image)

  - "!apps/proxy"   # Proxy service is also excluded

The root directory also houses global configuration files including package.json with workspace-wide scripts, turbo.json for task orchestration, and shared linting configurations. These files ensure consistent tooling standards across all workspace members while allowing individual apps to customize their specific build pipelines.

Apps Directory: The Application Layer

The apps/ folder contains standalone, runnable applications that represent the various user-facing and service components of Plane. Each application maintains its own build configuration, dependencies, and deployment strategy.

Web Application

The primary user interface lives in apps/web/ and is built with Vite. It uses @react-router/dev/vite for the development server and vite-tsconfig-paths to resolve TypeScript path aliases relative to its local tsconfig.json.

// https://github.com/makeplane/plane/blob/preview/apps/web/vite.config.ts
import { defineConfig } from "vite";
import { reactRouter } from "@react-router/dev/vite";
import tsconfigPaths from "vite-tsconfig-paths";
import path from "path";

export default defineConfig(() => ({
  plugins: [
    reactRouter(), 
    tsconfigPaths({ projects: [path.resolve(__dirname, "tsconfig.json")] })
  ],
  // …
}));

Admin and Space Interfaces

  • Admin UI (apps/admin/): Provides administrative controls with its own Vite configuration and MobX state management stores located in apps/admin/store/
  • Space UI (apps/space/): Handles workspace-specific functionality as a separate deployable unit

Live Collaboration Service

The apps/live/ directory contains a Node.js-based real-time server that handles WebSocket connections for collaborative features. Unlike the frontend apps, this service runs its own TypeScript build process and test suite independently.

Excluded Services

Both apps/api and apps/proxy are excluded from the JavaScript workspace because they are designed to run as containerized services with their own dependency management and build lifecycles.

Packages Directory: Shared Libraries

The packages/ directory houses reusable TypeScript libraries consumed by multiple applications. These modules enforce the Don't Repeat Yourself (DRY) principle across the Plane project monorepo structure.

Core Shared Modules

  • packages/utils/: Generic helpers for workspace handling, date manipulation, and data transformation
  • packages/logger/: Express middleware used by both frontend and backend services, exported from packages/logger/src/middleware.ts
  • packages/i18n/: Centralized internationalization containing locale JSON files (such as packages/i18n/src/locales/zh-CN/workspace.json) and translation utilities
  • packages/hooks/: Shared React hooks for data fetching and state synchronization
  • packages/editor/: Rich text editing components and utilities
  • packages/decorators/: TypeScript decorators for cross-cutting concerns

Cross-Cutting Logger Example

The logger package demonstrates how shared infrastructure code bridges frontend and backend concerns:

// https://github.com/makeplane/plane/blob/preview/packages/logger/src/middleware.ts
import { Request, Response, NextFunction } from "express";

export function requestLogger(req: Request, res: Response, next: NextFunction) {
  console.log(`${req.method} ${req.path}`);
  next();
}

Build Orchestration with TurboRepo

Plane leverages TurboRepo to execute tasks across the workspace in parallel while respecting dependency graphs. The root package.json defines high-level orchestration scripts that delegate to individual package scripts.

// https://github.com/makeplane/plane/blob/preview/package.json
{
  "scripts": {
    "dev": "turbo run dev --concurrency=18",
    "build": "turbo run build",
    "check": "turbo run check",
    "fix": "turbo run fix"
  }
}

Running pnpm dev initiates development servers for all workspace applications simultaneously—typically binding web to port 3000, admin to port 3001, and others—while watching for changes in shared packages/* dependencies. The --concurrency=18 flag ensures adequate parallel process allocation for the workspace size.

Linting, type-checking, and code formatting are similarly coordinated through Turbo. The pnpm check command runs validation across every package, while pnpm fix applies automated corrections using tools like oxlint throughout the repository.

Working with the Monorepo

Adding New Workspace Members

To extend the Plane project monorepo structure with new functionality:

  1. Create a directory under apps/ (for applications) or packages/ (for libraries)
  2. Initialize a package.json with a unique scoped name (e.g., "@plane/new-lib")
  3. Reference workspace dependencies using the workspace:* protocol
  4. Execute pnpm install to generate symlinks automatically

Importing Workspace Dependencies

Applications consume shared libraries via the workspace protocol, ensuring they always receive the local version during development:

// apps/web/src/components/example.tsx
import { formatDate } from "@plane/utils";

export const Example = () => <div>{formatDate(new Date())}</div>;

Targeted Script Execution

Developers can run commands for specific packages without navigating to their directories:


# Build only the admin UI

pnpm --filter @plane/admin run build

# Run type checking on the logger package only

pnpm --filter @plane/logger run check

Summary

  • pnpm workspaces define the monorepo boundaries in pnpm-workspace.yaml, including the apps/* and packages/* glob patterns while excluding containerized services like apps/api
  • Application isolation allows web, admin, space, and live to maintain independent Vite or Node.js build configurations while sharing code
  • Library reusability is enforced through packages/* modules including utils, logger, i18n, and hooks that export TypeScript code consumed via the workspace:* protocol
  • TurboRepo orchestration provides parallel execution of dev, build, check, and fix commands across the entire graph with intelligent caching
  • Developer ergonomics support filtering commands to specific packages and automatic symlink management when adding new workspace members

Frequently Asked Questions

How does Plane handle dependency management between apps and packages?

Plane uses pnpm's workspace protocol to link packages internally. When an app like web depends on @plane/utils, it specifies "@plane/utils": "workspace:*" in its package.json. During pnpm install, pnpm creates symlinks pointing to the local packages/utils directory, ensuring changes to shared code immediately reflect across consuming applications without publishing to npm.

Why are the API and Proxy services excluded from the pnpm workspace?

The apps/api and apps/proxy directories are excluded via the !apps/api and !apps/proxy patterns in pnpm-workspace.yaml because they are designed to run as containerized services with distinct runtime environments. These services manage their own dependencies and build processes within Docker images, separate from the JavaScript/TypeScript toolchain that powers the frontend applications and shared libraries.

What is the purpose of the live application in the monorepo?

The apps/live/ directory contains a Node.js real-time collaboration server that handles WebSocket connections for live editing and synchronization features. Unlike the static frontend applications built with Vite, this service requires a persistent Node process and is developed as a first-class workspace member, allowing it to import shared utilities like @plane/logger while maintaining its own TypeScript compilation and testing pipeline.

How can I run development servers for specific applications only?

Use pnpm's filter flag combined with Turbo to target specific workspace members. For example, pnpm --filter @plane/web run dev starts only the web application, while pnpm --filter @plane/web --filter @plane/admin run dev starts both the web and admin interfaces simultaneously. This approach conserves system resources when you only need to work on a subset of the Plane project monorepo structure.

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 →