GitHub
Core
Glass
Brutal
Humanist

Modal

Modal components display content in a layer above the main page. Use them for confirmations, forms, detailed information, or any interaction that requires focused user attention.

Modal Playground
Preview
Code
Controls
Size:
SM
MD
LG
XL
FULL
Close on Backdrop:
Header Description:
Header Icon:
Close Button:
Footer Left Content:

Installation

You can add the modal component to your project manually:

1

Install the following dependencies:

npm install class-variance-authority clsx tailwind-merge @phosphor-icons/react @react-aria/button @react-aria/focus @react-aria/interactions
2

Copy and paste the following code into your project.

Loading source code...
3

Update the import paths to match your project setup.

Update the import aliases (e.g. @/components, @/utils) in the copied files to match your project's path configuration.

Basic Usage

A simple modal with header, body, and footer:

import { useState } from 'react';
import { Modal } from '@versaui/ui/components/Modal';
import { Button } from '@versaui/ui/components/Button';
function Example() {
    const [isOpen, setIsOpen] = useState(false);
    return (
        <>
            <Button onClick={() => setIsOpen(true)}>Open Modal</Button>
            <Modal open={isOpen} onOpenChange={setIsOpen}>
                <Modal.Content>
                    <Modal.Header title="Confirm Action" />
                    <Modal.Body>
                        Are you sure you want to proceed?
                    </Modal.Body>
                    <Modal.Footer>
                        <Button variant="neutral" buttonStyle="subtle" onClick={() => setIsOpen(false)}>
                            Cancel
                        </Button>
                        <Button variant="primary" buttonStyle="filled">
                            Confirm
                        </Button>
                    </Modal.Footer>
                </Modal.Content>
            </Modal>
        </>
    );
}

Sizes

Five sizes are available — sm, md (default), lg, xl, and full:

<Modal open={isOpen} onOpenChange={setIsOpen} size="lg">
    <Modal.Content>
        <Modal.Header title="Large Modal" />
        <Modal.Body>Content with more horizontal space.</Modal.Body>
    </Modal.Content>
</Modal>
SizeMax Width
sm400px
md560px
lg720px
xl960px
full100vw - 48px

Header Variants

With Description

Add context with a description below the title:

<Modal.Header
    title="Create New Project"
    description="Fill in the details to get started"
/>

With Icon

Add a leading icon for visual emphasis:

import { Folder as FolderIcon } from '@phosphor-icons/react';
<Modal.Header
    title="Create New Project"
    icon={<FolderIcon />}
/>

Hide Close Button

For modals that require explicit action:

<Modal.Header
    title="Terms & Conditions"
    showCloseButton={false}
/>

Add metadata, checkboxes, or other content to the left side of the footer:

import { Checkbox } from '@versaui/ui/components/Checkbox';
<Modal.Footer
    leftContent={<Checkbox label="Don't show again" />}
>
    <Button variant="primary" buttonStyle="filled">Continue</Button>
</Modal.Footer>

Dismiss Behavior

Control how the modal can be closed:

// Prevent backdrop clicks from closing
<Modal
    open={isOpen}
    onOpenChange={setIsOpen}
    closeOnBackdrop={false}
>
    ...
</Modal>
// Prevent ESC key from closing
<Modal
    open={isOpen}
    onOpenChange={setIsOpen}
    closeOnEsc={false}
>
    ...
</Modal>

Scrollable Content

For long content, Modal.Body automatically scrolls while header and footer remain fixed:

<Modal open={isOpen} onOpenChange={setIsOpen}>
    <Modal.Content>
        <Modal.Header title="Terms of Service" sticky />
        <Modal.Body>
            {/* Long content here */}
        </Modal.Body>
        <Modal.Footer sticky>
            <Button variant="primary" buttonStyle="filled">Accept</Button>
        </Modal.Footer>
    </Modal.Content>
</Modal>

Accessibility

The Modal component is fully accessible:

  • Focus trap: Focus is trapped within the modal while open
  • Focus restoration: Returns focus to trigger element on close
  • Keyboard navigation: ESC closes the modal (configurable)
  • Screen readers: Proper aria-labelledby and aria-describedby associations
  • Body scroll lock: Background scroll is disabled while modal is open
PropTypeDefaultDescription
openbooleanfalseWhether the modal is open (controlled).
onOpenChange(open: boolean) => voidCalled when the open state changes.
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'Width of the modal.
closeOnBackdropbooleantrueClose when clicking the backdrop.
closeOnEscbooleantrueClose when pressing ESC key.
childrenReactNodeModal.Content and other children.
classNamestring''Additional CSS classes.

Modal.Header Props

PropTypeDefaultDescription
titlestringRequired. Modal title text.
descriptionstringOptional description below title.
iconReactNodeOptional leading icon element.
showCloseButtonbooleantrueShow the close button.
stickybooleanfalseStick to top when body scrolls.

Modal.Body Props

PropTypeDefaultDescription
childrenReactNodeBody content.
classNamestring''Additional CSS classes.

Modal.Footer Props

PropTypeDefaultDescription
childrenReactNodeAction buttons (right-aligned).
leftContentReactNodeLeft-aligned content.
stickybooleanfalseStick to bottom when body scrolls.

On this page