How Plane Organizes Apps and Packages in Its PNPM Monorepo Workspace

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. This file declares which directories PNPM should treat as workspace members:

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, 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 demonstrates how applications declare internal dependencies:

{
  "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, while date formatting utilities reside in 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:

// 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, 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 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 files. Applications extend these configurations in their local 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), 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.

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 →