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 + esbuild for 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" on Avatar, variant="primary" on Button, placement="top" on Tooltip
  • Composability – Tooltip wraps any element, ModalCore accepts arbitrary footer content
  • Tailwind compatibility – className props pass through to underlying elements
  • Type inference – SingleSelect infers option shape from the options array

Critical Source Files

File Responsibility
[packages/ui/package.json](https://github.com/makeplane/plane/blob/preview/packages/ui/package.json) Package manifest, dependency graph, build scripts
[packages/ui/src/index.ts](https://github.com/makeplane/plane/blob/preview/packages/ui/src/index.ts) Public API surface—every export consumers can import
[packages/ui/src/button/button.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/button/button.tsx) Core button with variants (primary, secondary, ghost, destructive)
[packages/ui/src/avatar/avatar.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/avatar/avatar.tsx) User avatar with fallback initials and image optimization
[packages/ui/src/modals/modal-core.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/modals/modal-core.tsx) Foundation for all modal dialogs
[packages/ui/src/modals/alert-modal.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/modals/alert-modal.tsx) Pre‑built confirmation dialog
[packages/ui/src/dropdown/single-select.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/dropdown/single-select.tsx) Accessible single‑option dropdown
[packages/ui/src/tooltip/tooltip.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/tooltip/tooltip.tsx) Hover/focus tooltips with smart positioning
[packages/ui/src/utils/classname.tsx](https://github.com/makeplane/plane/blob/preview/packages/ui/src/utils/classname.tsx) cn() utility wrapping tailwind-merge and clsx

Summary

  • @plane/ui is 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:

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 →