How Components Are Managed in the Plane Frontend: Inside the @plane/ui Monorepo Architecture

The Plane frontend centralizes its React components in a dedicated workspace package called @plane/ui, using a pnpm monorepo structure with path aliasing to enable type-safe, Tailwind-styled UI imports across multiple frontend applications.

The open-source project management tool Plane organizes its frontend code as a pnpm workspace monorepo, isolating reusable interface elements in a standalone package. This architecture ensures that buttons, modals, dropdowns, and layout components remain consistent across the web application and other client surfaces. Understanding how these pieces are authored, exported, and consumed reveals a sophisticated approach to component management that balances developer ergonomics with strict type safety.

Monorepo Architecture and the @plane/ui Package

At the heart of Plane's component strategy lies the @plane/ui workspace package, located at packages/ui in the repository root. This package functions as the single source of truth for all visual primitives, housing everything from simple buttons to complex search selects. By colocating these components in a shared library rather than duplicating them across application folders, the team maintains design consistency while enabling rapid iteration.

Component Organization and File Structure

Inside packages/ui/src, each UI element resides in its own directory with a predictable naming convention.

Directory Structure

Components follow a folder-per-feature pattern:

Type Definitions

Type safety is enforced at the component level. Files like packages/ui/src/button/helper.tsx export TypeScript types such as TButtonVariant and TButtonSizes, ensuring that prop autocompletion and validation propagate to every consumer.

Export Aggregation via the Central Index

Rather than requiring deep imports, the package exposes a clean public API through packages/ui/src/index.ts. This file aggregates exports from each component directory:

// packages/ui/src/index.ts
export * from "./button";
export * from "./modal";
export * from "./dropdown";
export * from "./modals";

This consolidation allows any application in the monorepo to import components from a single entry point: import { Button, ModalCore } from "@plane/ui".

Styling Strategy with Tailwind CSS

All components rely on Tailwind CSS utilities for styling, coordinated through a central configuration at packages/tailwind-config/tailwind.config.cjs.

The cn Utility

To handle conditional class merging, the UI package provides a cn helper in packages/ui/src/utils/index.ts. Components use this utility to compose Tailwind classes with consumer-provided overrides:

// packages/ui/src/button/button.tsx
const finalClassName = cn(buttonStyle, className);

Workspace Integration and Path Aliasing

The monorepo uses TypeScript path mapping to make @plane/ui resolvable from any application. The root tsconfig.json defines:

{
  "compilerOptions": {
    "paths": {
      "@plane/ui": ["packages/ui/src"]
    }
  }
}

This configuration eliminates relative path imports like ../../../packages/ui, allowing code in apps/web to reference components as if they were external npm packages.

Component Consumption Patterns

Frontend applications consume these components through direct named imports. For example, in apps/web/core/components/workspace/views/header.tsx:

import { Header, Avatar } from "@plane/ui";

const WorkspaceHeader = () => (
  <Header variant="transparent">
    <Avatar user={currentUser} size="md" />
  </Header>
);

Practical Implementation Examples

Basic Button Usage

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

export const SaveButton = () => (
  <Button variant="primary" size="lg" loading={false}>
    Save
  </Button>
);
import { ModalCore, Input, Header } from "@plane/ui";

export const RenameProjectModal = ({ isOpen, onClose }) => (
  <ModalCore isOpen={isOpen} onClose={onClose} title="Rename Project">
    <Header>Project name</Header>
    <Input placeholder="Enter new name" autoFocus />
  </ModalCore>
);

Complex Dropdown

import { CustomSearchSelect } from "@plane/ui";

export const UserSelect = ({ users, onSelect }) => (
  <CustomSearchSelect
    options={users.map(u => ({ label: u.name, value: u.id }))}
    onChange={onSelect}
    placeholder="Select a user"
  />
);

Build and Distribution

Because @plane/ui lives within the same repository as the consuming applications, changes propagate immediately through the pnpm build process without requiring separate npm publication. This co-location enables atomic commits that update both a component and its consumers simultaneously, ensuring type consistency across the entire frontend.

Summary

  • Plane isolates UI components in a dedicated workspace package at packages/ui
  • The index.ts barrel file aggregates exports, providing a clean import interface via @plane/ui
  • Components use TypeScript with forwardRef and ship with dedicated type definitions like TButtonVariant
  • Tailwind CSS handles styling through a cn utility for class composition located in packages/ui/src/utils/index.ts
  • Path aliasing in tsconfig.json enables seamless imports across the pnpm workspace
  • Over 100 component usages in apps/web demonstrate the pattern's effectiveness for large-scale React applications

Frequently Asked Questions

Where are Plane's UI components defined?

Plane's reusable UI components live in the @plane/ui workspace package under packages/ui/src. Each component has its own directory containing the implementation, types, and styles, such as packages/ui/src/button/button.tsx for button logic and packages/ui/src/dropdowns/custom-search-select.tsx for complex selection interfaces.

How does Plane handle component styling?

The frontend uses Tailwind CSS configured through packages/tailwind-config/tailwind.config.cjs. Components merge classes using a cn utility from packages/ui/src/utils/index.ts, allowing consistent application of design tokens while supporting consumer overrides through standard className props.

Can I use @plane/ui components outside the Plane repository?

Currently, @plane/ui is designed for internal consumption within the makeplane/plane monorepo. While the package structure theoretically supports npm publishing, the components are tightly coupled to the repository's Tailwind configuration and build pipeline, making external usage require additional setup and dependency management.

How are component changes propagated to the web application?

Because the repository uses pnpm workspaces with internal path aliasing, changes to packages/ui reflect immediately in consuming apps like apps/web after rebuilding. This eliminates version mismatch issues since all code shares the same commit history and build process, enabling atomic updates across the entire frontend.

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 →