What Is the @plane/ui Package? A Complete Guide to Plane's Shared UI Component Library
@plane/ui is the shared UI component library that provides type‑safe, reusable React components to all of Plane's front‑end applications, ensuring visual consistency across the monorepo.
The @plane/ui package sits at the heart of the makeplane/plane monorepo, serving as the single source of truth for every button, avatar, modal, and tooltip rendered in Plane's web, admin, live, and space applications. Built with TypeScript and Tailwind CSS, it bundles proven UI primitives from libraries like Blueprint, Headless UI, and Radix into a cohesive, tree‑shakable package that any Plane developer can import with a single line.
Purpose and Architecture of @plane/ui
The @plane/ui package solves a critical problem in large monorepos: UI fragmentation. Without a centralized component library, each application risks implementing its own buttons, dropdowns, and modals—leading to inconsistent styling, duplicated effort, and maintenance nightmares.
Key Design Principles
| Principle | Implementation | Source Location |
|---|---|---|
| Single source of truth | One barrel export exposes all components | [packages/ui/src/index.ts](https://github.com/makeplane/plane/blob/preview/packages/ui/src/index.ts) |
| Type safety | Every component written in TypeScript with explicit prop interfaces | Component files like [button/button.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/button/button.tsx) |
| Utility‑first styling | Tailwind CSS classes applied consistently | Throughout src/ folders |
| Tree‑shakable exports | Named exports allow bundlers to eliminate unused code | Barrel file structure |
| Peer dependency isolation | React and React DOM required from host, not bundled | [package.json peerDependencies](https://github.com/makeplane/plane/blob/preview/packages/ui/package.json) |
How @plane/ui Is Organized
The package follows a folder‑per‑feature structure. Each UI pattern lives in its own directory with co‑located implementation, types, and tests.
packages/ui/src/
├── index.ts # Barrel re‑export
├── avatar/ # User/image avatars
├── badge/ # Status and count badges
├── button/ # Primary, secondary, destructive buttons
├── dropdown/ # Select, multi‑select, combobox
├── modals/ # ModalCore, AlertModal, ConfirmModal
├── tooltip/ # Hover and focus tooltips
├── loader/ # Skeleton and spinner states
├── tables/ # Data table primitives
└── utils/ # Tailwind class merging utilities
This organization appears in the barrel file at [packages/ui/src/index.ts](https://github.com/makeplane/plane/blob/preview/packages/ui/src/index.ts), which re‑exports every sub‑module:
// From packages/ui/src/index.ts
export * from "./avatar";
export * from "./badge";
export * from "./button";
export * from "./dropdown";
export * from "./modals";
// ... etc
Consumers import exactly what they need:
import { Button, Avatar, ModalCore } from '@plane/ui';
Dependencies and External Integrations
The @plane/ui package strategically blends internal Plane utilities with battle‑tested third‑party primitives.
Internal Dependencies
@plane/constants– Shared design tokens and magic values@plane/hooks– Reusable React hooks (focus traps, click‑outside, etc.)@plane/utils– Helper functions for formatting and validation
External UI Primitives
| Library | Purpose | Example Usage |
|---|---|---|
@blueprintjs/core |
Complex overlays, non‑ideal states | Popover2 internals |
@headlessui/react |
Accessible, unstyled components | Dropdown transitions |
@radix-ui/react-scroll‑area |
Custom scrollable regions | Scrollable modals and lists |
lucide-react |
Consistent iconography | Button icons |
tailwind-merge |
Class name deduplication | [utils/classname.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/utils/classname.tsx) |
The peer dependency declaration for react and react-dom ensures that host applications supply their own React version, preventing the "multiple React copies" error common in monorepo setups.
Build and Developer Experience
The [package.json](https://github.com/makeplane/plane/blob/preview/packages/ui/package.json) scripts reveal a modern, fast toolchain:
{
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"lint": "oxlint",
"format": "oxfmt"
}
}
- tsdown: Tiny wrapper around
tsc+esbuildfor rapid TypeScript compilation - oxlint/oxfmt: Rust‑based linter and formatter for sub‑second feedback loops
- Storybook: Component documentation and visual testing (implied by dependencies)
Practical Usage Example
Here's how @plane/ui components compose together in a real Plane feature:
import React, { useState } from 'react';
import {
Button,
Avatar,
Tooltip,
ModalCore,
SingleSelect,
} from '@plane/ui';
export const IssueCreator = () => {
const [open, setOpen] = useState(false);
const [issueType, setIssueType] = useState(null);
const typeOptions = [
{ value: 'bug', label: 'Bug', color: '#ef4444' },
{ value: 'feature', label: 'Feature', color: '#3b82f6' },
{ value: 'improvement', label: 'Improvement', color: '#22c55e' },
];
return (
<div className="flex items-center gap-3 p-4">
{/* Current user avatar */}
<Avatar
src="/api/users/me/avatar"
alt="Current user"
size="md"
fallback="👤"
/>
{/* Primary action with contextual help */}
<Tooltip content="Create a new issue (⌘N)" placement="top">
<Button
variant="primary"
onClick={() => setOpen(true)}
>
New Issue
</Button>
</Tooltip>
{/* Issue type selector */}
<SingleSelect
options={typeOptions}
value={issueType}
onChange={setIssueType}
placeholder="Select type..."
className="w-48"
/>
{/* Creation modal */}
{open && (
<ModalCore
title="Create new issue"
onClose={() => setOpen(false)}
size="lg"
footer={
<div className="flex justify-end gap-2">
<Button variant="secondary" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button variant="primary" onClick={handleCreate}>
Create Issue
</Button>
</div>
}
>
<p className="text-gray-600">
Fill in the details for your {issueType?.label?.toLowerCase() || 'issue'}.
</p>
</ModalCore>
)}
</div>
);
};
Key patterns demonstrated:
- Consistent prop interfaces –
size="md"onAvatar,variant="primary"onButton,placement="top"onTooltip - Composability –
Tooltipwraps any element,ModalCoreaccepts arbitrary footer content - Tailwind compatibility –
classNameprops pass through to underlying elements - Type inference –
SingleSelectinfers option shape from theoptionsarray
Critical Source Files
Summary
@plane/uiis Plane's shared React component library, consumed by all front‑end applications in the monorepo.- TypeScript + Tailwind CSS provide type safety and consistent styling without CSS‑in‑JS overhead.
- Barrel exports from [
src/index.ts](https://github.com/makeplane/plane/blob/preview/packages/ui/src/index.ts) enable clean, tree‑shakable imports. - Hybrid dependency strategy combines internal Plane packages with proven external primitives (Blueprint, Headless UI, Radix).
- Peer dependencies on React keep the bundle lightweight and prevent version conflicts.
- Modern toolchain (tsdown, oxlint, oxfmt) delivers fast build and feedback cycles.
Frequently Asked Questions
What is the entry point for importing @plane/ui components?
The entry point is [packages/ui/src/index.ts](https://github.com/makeplane/plane/blob/preview/packages/ui/src/index.ts), a barrel file that re‑exports every component folder. This allows consumers to write import { Button, Avatar } from '@plane/ui' rather than deep‑importing individual files. The package.json specifies "main": "dist/index.js" and "types": "dist/index.d.ts" so TypeScript and bundlers resolve correctly.
Does @plane/ui bundle its own copy of React?
No. The [package.json](https://github.com/makeplane/plane/blob/preview/packages/ui/package.json) declares react and react-dom as peer dependencies, not regular dependencies. This means the host application (web, admin, space, etc.) provides the React instance. This design prevents the "Hooks can only be called inside the body of a function component" errors that occur when multiple React copies exist in a single runtime.
How does @plane/ui handle Tailwind class conflicts?
The package includes a cn() utility in [src/utils/classname.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/utils/classname.tsx) that wraps tailwind-merge and clsx. This function intelligently merges Tailwind classes, resolving conflicts like p-2 p-4 to p-4 and handling conditional class application. Component authors use this internally, and consumers can rely on predictable class resolution when extending components.
Can I use @plane/ui outside of the Plane monorepo?
Technically yes, but practically no. While the package is published‑ready with proper exports and peer dependencies, it depends heavily on internal Plane packages (@plane/constants, @plane/hooks, @plane/utils) and assumes specific Tailwind configuration conventions. Extracting it for external use would require either publishing those dependencies or refactoring the components to remove Plane‑specific logic.
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 →