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

> Discover how the @plane/ui monorepo architecture manages React components in the Plane frontend. Learn about pnpm, path aliasing, and efficient UI imports for multiple applications.

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

---

**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:

- [`packages/ui/src/button/button.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/button/button.tsx) contains the Button implementation with `forwardRef` support
- [`packages/ui/src/modals/modal-core.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/modals/modal-core.tsx) provides the base ModalCore component  
- [`packages/ui/src/dropdowns/custom-search-select.tsx`](https://github.com/makeplane/plane/blob/main/packages/ui/src/dropdowns/custom-search-select.tsx) implements complex dropdown logic

### Type Definitions

Type safety is enforced at the component level. Files like [`packages/ui/src/button/helper.tsx`](https://github.com/makeplane/plane/blob/main/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`](https://github.com/makeplane/plane/blob/main/packages/ui/src/index.ts). This file aggregates exports from each component directory:

```tsx
// 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`](https://github.com/makeplane/plane/blob/main/packages/ui/src/utils/index.ts). Components use this utility to compose Tailwind classes with consumer-provided overrides:

```tsx
// 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`](https://github.com/makeplane/plane/blob/main/tsconfig.json) defines:

```json
{
  "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`](https://github.com/makeplane/plane/blob/main/apps/web/core/components/workspace/views/header.tsx):

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

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

```

## Practical Implementation Examples

### Basic Button Usage

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

export const SaveButton = () => (
  <Button variant="primary" size="lg" loading={false}>
    Save
  </Button>
);

```

### Modal Composition

```tsx
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

```tsx
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`](https://github.com/makeplane/plane/blob/main/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`](https://github.com/makeplane/plane/blob/main/packages/ui/src/utils/index.ts)
- Path aliasing in [`tsconfig.json`](https://github.com/makeplane/plane/blob/main/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`](https://github.com/makeplane/plane/blob/main/packages/ui/src/button/button.tsx) for button logic and [`packages/ui/src/dropdowns/custom-search-select.tsx`](https://github.com/makeplane/plane/blob/main/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`](https://github.com/makeplane/plane/blob/main/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.