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:
packages/ui/src/button/button.tsxcontains the Button implementation withforwardRefsupportpackages/ui/src/modals/modal-core.tsxprovides the base ModalCore componentpackages/ui/src/dropdowns/custom-search-select.tsximplements complex dropdown logic
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>
);
Modal Composition
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.tsbarrel file aggregates exports, providing a clean import interface via@plane/ui - Components use TypeScript with
forwardRefand ship with dedicated type definitions likeTButtonVariant - Tailwind CSS handles styling through a
cnutility for class composition located inpackages/ui/src/utils/index.ts - Path aliasing in
tsconfig.jsonenables seamless imports across the pnpm workspace - Over 100 component usages in
apps/webdemonstrate 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →