# How Plane Organizes Apps and Packages in Its PNPM Monorepo Workspace

> Understand how Plane organizes its pnpm monorepo workspace. Discover how apps and packages are structured within the codebase and learn how non-Node.js services are managed.

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

---

**Plane structures its codebase as a PNPM monorepo where `apps/*` contains deployable applications and `packages/*` houses shared libraries, with non-Node.js services explicitly excluded from the workspace.**

The open-source project management platform Plane manages its TypeScript frontend architecture using a PNPM monorepo workspace. This configuration centralizes shared code while allowing each application to maintain independent build tooling and deployment lifecycles.

## Workspace Configuration in pnpm-workspace.yaml

The monorepo boundary is defined at the repository root in **[`pnpm-workspace.yaml`](https://github.com/makeplane/plane/blob/main/pnpm-workspace.yaml)**. This file declares which directories PNPM should treat as workspace members:

```yaml
packages:
  - apps/*
  - packages/*
  - "!apps/api"
  - "!apps/proxy"

```

The `apps/*` and `packages/*` globs instruct PNPM to treat every subdirectory under these paths as an individual workspace member. The exclusions (`!apps/api` and `!apps/proxy`) deliberately omit the Django backend server and Nginx proxy configuration from the JavaScript dependency tree, as these services do not use Node.js or PNPM.

## App Structure Under apps/

Each folder under **`apps/`** represents a standalone application with its own [`package.json`](https://github.com/makeplane/plane/blob/main/package.json), build configuration, and deployment pipeline. These applications consume shared packages via the `workspace:*` protocol.

### Key Applications and Their Roles

- **`apps/web`** – The primary React single-page application built with Vite, serving as the main client interface.
- **`apps/admin`** – A dedicated administrative panel using React and Vite for management functions.
- **`apps/space`** – A specialized workspace UI application with its own routing and state management.
- **`apps/live`** – A Node.js-based real-time collaboration server handling WebSocket connections.

The **[`apps/web/package.json`](https://github.com/makeplane/plane/blob/main/apps/web/package.json)** demonstrates how applications declare internal dependencies:

```json
{
  "dependencies": {
    "@plane/constants": "workspace:*",
    "@plane/editor": "workspace:*",
    "@plane/hooks": "workspace:*",
    "@plane/i18n": "workspace:*",
    "@plane/propel": "workspace:*",
    "@plane/services": "workspace:*",
    "@plane/shared-state": "workspace:*",
    "@plane/types": "workspace:*",
    "@plane/ui": "workspace:*",
    "@plane/utils": "workspace:*",
    "@plane/tailwind-config": "workspace:*",
    "@plane/typescript-config": "workspace:*"
  }
}

```

## Package Structure Under packages/

All reusable code lives under **`packages/`**, organized by technical concern. Each package compiles independently (typically using `tsup` or `vite`) and exposes specific functionality to consuming applications.

### Shared Libraries Overview

| Package | Path | Purpose |
|---------|------|---------|
| **utils** | `packages/utils` | General-purpose helpers for strings, files, dates, and emoji handling. |
| **constants** | `packages/constants` | Enum definitions, theme constants, and UI configuration values. |
| **hooks** | `packages/hooks` | React hooks such as `useLocalStorage` and `useOutsideClickDetector`. |
| **i18n** | `packages/i18n` | Internationalization utilities and locale data management. |
| **editor** | `packages/editor` | Rich-text editor components and associated styling. |
| **types** | `packages/types` | Centralized TypeScript declarations re-exported as `@plane/types`. |
| **shared-state** | `packages/shared-state` | MobX stores for cross-application state management. |
| **tailwind-config** | `packages/tailwind-config` | Shared Tailwind CSS configuration presets. |
| **typescript-config** | `packages/typescript-config` | Base TypeScript compiler configurations inherited by apps. |

For example, workspace constants are defined in **[`packages/constants/src/workspace.ts`](https://github.com/makeplane/plane/blob/main/packages/constants/src/workspace.ts)**, while date formatting utilities reside in **[`packages/utils/src/date.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/date.ts)**.

## Cross-Package Dependency Resolution

PNPM creates a virtual `node_modules` structure for each application, automatically linking internal packages via the **`workspace:*`** protocol. This approach provides several architectural benefits:

- **Zero-publish workflow** – Changes to `packages/utils` are immediately available to `apps/web` without npm publishing or version bumping.
- **Single source of truth** – The **`packages/types`** module centralizes API interfaces, ensuring all applications reference identical TypeScript definitions.
- **Deduplicated builds** – Shared packages compile once, and each application consumes the same JavaScript bundle, reducing runtime overhead.

## Practical Implementation Example

Applications import shared functionality using scoped package names:

```tsx
// apps/web/src/components/Example.tsx
import { formatDate } from '@plane/utils';

function TimestampDisplay({ isoString }: { isoString: string }) {
  return <time dateTime={isoString}>{formatDate(isoString)}</time>;
}

```

This import resolves directly to **[`packages/utils/src/date.ts`](https://github.com/makeplane/plane/blob/main/packages/utils/src/date.ts)**, with PNPM handling the filesystem link during development and the build-time resolution for production bundles.

## Summary

- Plane defines its monorepo scope in **[`pnpm-workspace.yaml`](https://github.com/makeplane/plane/blob/main/pnpm-workspace.yaml)** using `apps/*` and `packages/*` globs, explicitly excluding Django and Nginx directories.
- Applications under **`apps/`** (web, admin, space, live) maintain independent configurations while consuming shared libraries.
- Packages under **`packages/`** provide utilities, constants, types, and UI components via the `workspace:*` protocol.
- TypeScript configurations and Tailwind presets are distributed as internal packages to ensure consistency across the codebase.

## Frequently Asked Questions

### What does the `workspace:*` protocol do in Plane's monorepo?

The `workspace:*` protocol tells PNPM to resolve the dependency from the local filesystem rather than the npm registry. In Plane, this links applications like `apps/web` directly to source code in `packages/`, enabling instantaneous reflection of changes without publishing packages or updating version numbers.

### Why are `apps/api` and `apps/proxy` excluded from the PNPM workspace?

These directories contain the **Django** backend API and **Nginx** proxy configuration, respectively. Since they do not use Node.js or require JavaScript dependency management, excluding them prevents PNPM from attempting to install Node modules in these directories and keeps the JavaScript workspace focused on the frontend codebase.

### How are TypeScript configurations managed across the monorepo?

Plane distributes shared compiler settings through **`packages/typescript-config`**, which contains base [`tsconfig.json`](https://github.com/makeplane/plane/blob/main/tsconfig.json) files. Applications extend these configurations in their local [`tsconfig.json`](https://github.com/makeplane/plane/blob/main/tsconfig.json) files, ensuring consistent strictness rules and path aliases while allowing app-specific overrides for build targets.

### What build tools does Plane use for apps versus packages?

Applications typically use **Vite** for bundling (evident in [`apps/web/vite.config.ts`](https://github.com/makeplane/plane/blob/main/apps/web/vite.config.ts)), while shared packages often use **tsup** or lighter TypeScript compilation. This separation allows applications to benefit from Vite's hot module replacement and optimization, while packages prioritize fast compilation and minimal bundle sizes for library distribution.