Skip to main content
The @proton/components package is the comprehensive React component library containing UI components, containers, and hooks used to build all Proton web applications.

Installation

yarn add @proton/components
This package has several peer dependencies that must be installed:
yarn add @proton/shared @proton/crypto @proton/key-transparency @proton/srp @proton/cross-storage date-fns

Package Information

{
  "name": "@proton/components",
  "license": "GPL-3.0",
  "sideEffects": false,
  "main": "index.ts"
}

Architecture

The package is organized into three main directories:
Presentational components - Reusable UI elements
  • Buttons, inputs, modals, dropdowns
  • Layout components
  • Data display components
  • Generally stateless or with minimal local state

Component Categories

Form Components

Input Fields

Text inputs, email inputs, password fields with validation

Select & Dropdowns

Dropdowns, autocomplete, multi-select components

Buttons

Primary, secondary, ghost, icon buttons with loading states

Form Controls

Checkboxes, radio buttons, toggles, switches

Common Components

import {
  Alert,
  Badge,
  Button,
  ButtonGroup,
  Dropdown,
  DropdownMenu,
  DropdownMenuButton,
  Icon,
  Info,
  InputFieldTwo,
  Label,
  Loader,
  Modal,
  ModalTwo,
  Option,
  PrimaryButton,
  SelectTwo,
  Tooltip,
} from '@proton/components';

Button Examples

import { Button, PrimaryButton } from '@proton/components';

// Basic button
<Button onClick={handleClick}>Click me</Button>

// Primary button
<PrimaryButton loading={isLoading}>Submit</PrimaryButton>

// Button with icon
<Button icon={<Icon name="plus" />}>Add item</Button>

// Disabled button
<Button disabled>Disabled</Button>
import { ModalTwo, ModalTwoContent, ModalTwoHeader, ModalTwoFooter, Button } from '@proton/components';

function MyModal({ open, onClose }) {
  return (
    <ModalTwo open={open} onClose={onClose}>
      <ModalTwoHeader title="Confirmation" />
      <ModalTwoContent>
        <p>Are you sure you want to proceed?</p>
      </ModalTwoContent>
      <ModalTwoFooter>
        <Button onClick={onClose}>Cancel</Button>
        <PrimaryButton onClick={handleConfirm}>Confirm</PrimaryButton>
      </ModalTwoFooter>
    </ModalTwo>
  );
}

Layout Components

import {
  AppContainer,
  Sidebar,
  Header,
  PrivateMainArea,
  TopNavbar,
  SettingsContainer,
  Section,
  Row,
  Field,
  Label,
} from '@proton/components';

Layout Example

import { AppContainer, Sidebar, PrivateMainArea } from '@proton/components';

function App() {
  return (
    <AppContainer>
      <Sidebar>
        {/* Navigation items */}
      </Sidebar>
      <PrivateMainArea>
        {/* Main content */}
      </PrivateMainArea>
    </AppContainer>
  );
}

Data Display

import { Table, TableBody, TableRow, TableCell } from '@proton/components';

<Table>
  <TableBody>
    {items.map(item => (
      <TableRow key={item.id}>
        <TableCell>{item.name}</TableCell>
        <TableCell>{item.value}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>
import { OrderableTable } from '@proton/components';

<OrderableTable
  items={items}
  onSortEnd={handleSortEnd}
  renderItem={(item) => (
    <div>{item.name}</div>
  )}
/>
import { Badge, BetaBadge } from '@proton/components';

<Badge type="success">Active</Badge>
<Badge type="error">Failed</Badge>
<Badge type="warning">Pending</Badge>
<BetaBadge />

Container Components

Containers integrate with APIs and state management:
import {
  AddressesSection,
  PasswordSection,
  RecoveryMethodsSection,
  SubscriptionSection,
  UsernameSection,
  SecuritySection,
} from '@proton/components';

Settings Containers

import { SettingsContainer, UsernameSection, PasswordSection } from '@proton/components';

function AccountSettings() {
  return (
    <SettingsContainer>
      <UsernameSection />
      <PasswordSection />
      {/* More settings sections */}
    </SettingsContainer>
  );
}

Custom Hooks

API Hooks

import {
  useApi,
  useApiResult,
  useApiWithoutResult,
  useCachedModelResult,
  useGetAddresses,
  useGetUser,
  useGetUserKeys,
} from '@proton/components';

Example Usage

import { useGetUser, useGetAddresses, useApi } from '@proton/components';
import { updateUsername } from '@proton/shared/lib/api/settings';

function UserProfile() {
  const api = useApi();
  const [user, loadingUser] = useGetUser();
  const [addresses] = useGetAddresses();
  
  const handleUpdateUsername = async (newUsername: string) => {
    await api(updateUsername(newUsername));
  };
  
  if (loadingUser) {
    return <Loader />;
  }
  
  return (
    <div>
      <h1>{user.Name}</h1>
      <p>{addresses[0]?.Email}</p>
    </div>
  );
}

Authentication Hooks

import {
  useAuthentication,
  useUser,
  useUserSettings,
  useOrganization,
  useSubscription,
} from '@proton/components';

Feature Flag Hooks

import { useFeature, useEarlyAccess } from '@proton/components';

function NewFeature() {
  const { feature, loading } = useFeature('FeatureCode');
  
  if (loading || !feature?.Value) {
    return null;
  }
  
  return <div>New feature enabled!</div>;
}

Form Handling

Components use Formik for form management:
import { useFormik } from 'formik';
import { InputFieldTwo, Button } from '@proton/components';

function LoginForm() {
  const formik = useFormik({
    initialValues: {
      username: '',
      password: '',
    },
    onSubmit: async (values) => {
      // Handle submission
    },
  });
  
  return (
    <form onSubmit={formik.handleSubmit}>
      <InputFieldTwo
        id="username"
        label="Username"
        value={formik.values.username}
        onChange={formik.handleChange}
        error={formik.errors.username}
      />
      <InputFieldTwo
        id="password"
        type="password"
        label="Password"
        value={formik.values.password}
        onChange={formik.handleChange}
        error={formik.errors.password}
      />
      <Button type="submit" loading={formik.isSubmitting}>
        Login
      </Button>
    </form>
  );
}

Notifications

import { useNotifications } from '@proton/components';

function MyComponent() {
  const { createNotification } = useNotifications();
  
  const handleSuccess = () => {
    createNotification({
      type: 'success',
      text: 'Operation completed successfully',
    });
  };
  
  const handleError = (error: Error) => {
    createNotification({
      type: 'error',
      text: error.message,
    });
  };
}

Drawer System

Integrated drawer for quick actions:
import {
  DrawerApp,
  DrawerSidebar,
  CalendarDrawerAppButton,
  ContactDrawerAppButton,
  SecurityCenterDrawerAppButton,
} from '@proton/components';

Dependencies

{
  "@proton/account": "workspace:^",
  "@proton/atoms": "workspace:^",
  "@proton/calendar": "workspace:^",
  "@proton/features": "workspace:^",
  "@proton/icons": "workspace:^",
  "@proton/mail": "workspace:^",
  "@proton/styles": "workspace:^"
}

Testing

# Run tests
yarn workspace @proton/components test

# Run tests with coverage
yarn workspace @proton/components test:ci

# Type checking
yarn workspace @proton/components check-types

Testing Components

import { render, screen } from '@testing-library/react';
import { Button } from '@proton/components';

test('renders button with text', () => {
  render(<Button>Click me</Button>);
  expect(screen.getByText('Click me')).toBeInTheDocument();
});

Best Practices

Peer Dependencies: Ensure all peer dependencies are installed to avoid runtime errors.
Component Selection:
  • Use @proton/atoms for basic UI elements
  • Use @proton/components for complex, feature-rich components
  • Create custom components in your app for app-specific logic
Styling: Components use SCSS from @proton/styles. Import styles in your application entry point.

Migration Guide

1

ModalTwo vs Modal

Prefer ModalTwo for new code. It has better accessibility and mobile support.
2

InputFieldTwo vs Input

Use InputFieldTwo for form fields. It includes built-in label and error handling.
3

SelectTwo vs Select

SelectTwo provides better keyboard navigation and accessibility.

Build docs developers (and LLMs) love