Skip to main content

Modal Components

Proton provides a comprehensive modal system (ModalTwo) for displaying dialogs, confirmations, and overlay content. The main modal component with animation and accessibility features. Location: components/modalTwo/Modal.tsx

Basic Usage

import { useState } from 'react';
import { Button } from '@proton/atoms/Button/Button';
import Modal from '@proton/components/components/modalTwo/Modal';
import ModalHeader from '@proton/components/components/modalTwo/ModalHeader';
import ModalContent from '@proton/components/components/modalTwo/ModalContent';
import ModalFooter from '@proton/components/components/modalTwo/ModalFooter';

const MyComponent = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setOpen(true)}>Open Modal</Button>
      
      <Modal open={open} onClose={() => setOpen(false)} size="small">
        <ModalHeader title="My Modal" />
        <ModalContent>
          <p>Modal content goes here</p>
        </ModalContent>
        <ModalFooter>
          <Button onClick={() => setOpen(false)} color="weak">Cancel</Button>
          <Button onClick={() => setOpen(false)} color="norm">Confirm</Button>
        </ModalFooter>
      </Modal>
    </>
  );
};

Props

open
boolean
Whether the modal is open or not
onClose
() => void
Callback when user clicks close button or presses Escape key
size
ModalSize
Modal size. Options: xsmall, small, medium, large, xlarge, full. Default: medium
fullscreen
boolean
Make modal fullscreen
fullscreenOnMobile
boolean
Make modal fullscreen on mobile devices only
disableCloseOnEscape
boolean
Disables closing the modal by pressing the Escape key
enableCloseWhenClickOutside
boolean
Allow closing modal by clicking outside of it
onEnter
() => void
Callback when modal has finished its enter animation
onExit
() => void
Callback when modal has finished its exit animation
onBackdropClick
() => void
Callback when user clicks on the backdrop outside the dialog
blurBackdrop
boolean
Whether or not to blur the backdrop of the modal
behind
boolean
Whether the modal should render behind the backdrop
id
string
Optional id to overwrite the internally generated id (used for accessibility)
rootClassName
string
Additional classname on the root element
hotkeys
HotkeyTuple[]
Extra hotkeys definition for modals

Examples

<Modal open={open} onClose={() => setOpen(false)}>
  <ModalHeader title="Basic Modal" />
  <ModalContent>
    <p>This is a basic modal dialog.</p>
  </ModalContent>
  <ModalFooter>
    <Button onClick={() => setOpen(false)}>Close</Button>
  </ModalFooter>
</Modal>

ModalHeader

Header component for modals with title and close button. Location: components/modalTwo/ModalHeader.tsx

Usage

import ModalHeader from '@proton/components/components/modalTwo/ModalHeader';

<ModalHeader 
  title="My Modal Title"
  subline="Optional subtitle"
  actions={<Button size="small">Action</Button>}
/>

Props

title
string | ReactNode
The modal title
subline
string | ReactNode
Optional subtitle or description
actions
ReactNode
Optional action buttons to display in the header
hasClose
boolean
Whether to show the close button. Default: true
displayTitle
boolean
Whether to display the title. Default: true

ModalContent

Content wrapper for modal body. Location: components/modalTwo/ModalContent.tsx

Usage

import ModalContent from '@proton/components/components/modalTwo/ModalContent';

<ModalContent>
  <p>Your modal content here</p>
</ModalContent>

ModalFooter

Footer component for modal actions. Location: components/modalTwo/ModalFooter.tsx

Usage

import ModalFooter from '@proton/components/components/modalTwo/ModalFooter';

<ModalFooter>
  <Button color="weak">Cancel</Button>
  <Button color="norm">Confirm</Button>
</ModalFooter>

useModalState

Hook for managing modal state. Location: components/modalTwo/useModalState.ts

Usage

import useModalState from '@proton/components/components/modalTwo/useModalState';

const MyComponent = () => {
  const [modalState, setModalOpen, setModalClosed] = useModalState();

  return (
    <>
      <Button onClick={setModalOpen}>Open Modal</Button>
      
      <Modal 
        open={modalState} 
        onClose={setModalClosed}
      >
        <ModalContent>Modal content</ModalContent>
      </Modal>
    </>
  );
};

useModalTwo

Advanced hook for modal management with render props. Location: components/modalTwo/useModalTwo.tsx

Usage

import { useModalTwo } from '@proton/components/components/modalTwo/useModalTwo';

const MyComponent = () => {
  const [modal, showModal] = useModalTwo(MyModalComponent);

  return (
    <>
      <Button onClick={showModal}>Show Modal</Button>
      {modal}
    </>
  );
};

BasicModal

Simplified modal component for basic use cases. Location: components/modalTwo/BasicModal.tsx

Usage

import BasicModal from '@proton/components/components/modalTwo/BasicModal';

<BasicModal
  isOpen={open}
  onClose={() => setOpen(false)}
  title="Simple Modal"
>
  <p>This is a basic modal with minimal configuration.</p>
</BasicModal>

Best Practices

Use state hooks to manage modal open/close:
const [isOpen, setIsOpen] = useState(false);
const [data, setData] = useState(null);

const handleOpen = (itemData) => {
  setData(itemData);
  setIsOpen(true);
};

const handleClose = () => {
  setIsOpen(false);
  setData(null);
};

return (
  <Modal open={isOpen} onClose={handleClose}>
    <ModalContent>{/* Use data here */}</ModalContent>
  </Modal>
);

Accessibility

// Provide meaningful titles
<ModalHeader title="Delete Account" />

// Use proper button roles and labels
<ModalFooter>
  <Button onClick={onClose} aria-label="Cancel deletion">
    Cancel
  </Button>
  <Button onClick={onDelete} color="danger" aria-label="Confirm deletion">
    Delete
  </Button>
</ModalFooter>

Size Selection

  • xsmall: Simple confirmations, alerts
  • small: Basic forms, short content
  • medium: Standard forms, moderate content (default)
  • large: Complex forms, detailed content
  • xlarge: Very complex content
  • full: Full-screen experiences
// Confirmation dialog
<Modal size="small" />

// Standard form
<Modal size="medium" />

// Complex settings
<Modal size="large" />

Loading States

const [loading, setLoading] = useState(false);

const handleSubmit = async () => {
  setLoading(true);
  try {
    await saveData();
    onClose();
  } catch (error) {
    // Handle error
  } finally {
    setLoading(false);
  }
};

return (
  <Modal open={open} onClose={onClose}>
    <ModalHeader title="Save Changes" />
    <ModalContent>
      {/* Form content */}
    </ModalContent>
    <ModalFooter>
      <Button disabled={loading} onClick={onClose}>Cancel</Button>
      <Button loading={loading} onClick={handleSubmit}>Save</Button>
    </ModalFooter>
  </Modal>
);

Preventing Accidental Closes

<Modal
  open={hasUnsavedChanges}
  onClose={handleAttemptClose}
  disableCloseOnEscape={hasUnsavedChanges}
  onBackdropClick={hasUnsavedChanges ? undefined : onClose}
>
  {/* Modal content */}
</Modal>

Common Patterns

Confirmation Modal

const ConfirmationModal = ({ open, onClose, onConfirm, title, message }) => {
  return (
    <Modal open={open} onClose={onClose} size="small">
      <ModalHeader title={title} />
      <ModalContent>
        <p>{message}</p>
      </ModalContent>
      <ModalFooter>
        <Button onClick={onClose} color="weak">Cancel</Button>
        <Button onClick={onConfirm} color="danger">Confirm</Button>
      </ModalFooter>
    </Modal>
  );
};

Multi-Step Modal

const MultiStepModal = ({ open, onClose }) => {
  const [step, setStep] = useState(1);

  return (
    <Modal open={open} onClose={onClose}>
      <ModalHeader title={`Step ${step} of 3`} />
      <ModalContent>
        {step === 1 && <Step1 />}
        {step === 2 && <Step2 />}
        {step === 3 && <Step3 />}
      </ModalContent>
      <ModalFooter>
        {step > 1 && (
          <Button onClick={() => setStep(step - 1)}>Back</Button>
        )}
        {step < 3 ? (
          <Button onClick={() => setStep(step + 1)} color="norm">Next</Button>
        ) : (
          <Button onClick={handleFinish} color="norm">Finish</Button>
        )}
      </ModalFooter>
    </Modal>
  );
};

Source Code

View source:
  • Modal: packages/components/components/modalTwo/Modal.tsx:1
  • ModalHeader: packages/components/components/modalTwo/ModalHeader.tsx:1
  • useModalState: packages/components/components/modalTwo/useModalState.ts:1

Build docs developers (and LLMs) love