Modal

A dialog component for displaying content in an overlay. Built on Radix UI Dialog for accessibility and focus management.

State Variants (Horizontal)

Vertical Alignment

Footer Additions

Basic Usage

import { Modal } from '@/components/Modal'
import { Button } from '@/components/Button'

<Modal>
  <Modal.Trigger asChild>
    <Button>Open Modal</Button>
  </Modal.Trigger>
  <Modal.Content>
    <Modal.Header
      state="warning"
      title="Confirm Action"
      description="Are you sure you want to proceed?"
    />
    <Modal.Footer>
      <Button variant="secondary" appearance="stroke">Cancel</Button>
      <Button variant="primary">Continue</Button>
    </Modal.Footer>
  </Modal.Content>
</Modal>

State Variants

The Modal supports different states for various use cases.

Default

Standard modal without any state indicator.

<Modal.Header
  state="default"
  title="Basic Modal"
  description="This is a simple modal dialog."
/>

Error

For destructive actions or error messages.

<Modal.Header
  state="error"
  title="Delete Item"
  description="This action cannot be undone."
/>
<Modal.Footer>
  <Button variant="secondary" appearance="stroke">Cancel</Button>
  <Button variant="destructive">Delete</Button>
</Modal.Footer>

Warning

For caution messages and confirmations.

<Modal.Header
  state="warning"
  title="Unsaved Changes"
  description="You have unsaved changes that will be lost."
/>

Success

For success messages and confirmations.

<Modal.Header
  state="success"
  title="Payment Complete"
  description="Your transaction was processed successfully."
/>

Info

For informational messages.

<Modal.Header
  state="info"
  title="New Features"
  description="Check out the latest updates to your account."
/>

Alignment

The Modal header supports two alignment options.

Horizontal (Default)

Icon on the left, close button on the right.

<Modal.Header
  alignment="horizontal"
  state="warning"
  title="Confirm Action"
  description="Are you sure you want to proceed?"
  showCloseButton
/>

Vertical

Centered content, ideal for focused dialogs.

<Modal.Header
  alignment="vertical"
  state="success"
  title="Success!"
  description="Your changes have been saved."
/>

Custom Icon

Override the default state icon with a custom icon.

<Modal.Header
  state="default"
  title="Settings"
  description="Configure your preferences."
  icon={<SettingsIcon />}
/>

Footer Layouts

The Modal footer supports different button layouts.

Double (Default)

Two buttons with equal width.

<Modal.Footer layout="double">
  <Button variant="secondary" appearance="stroke" className="flex-1">
    Cancel
  </Button>
  <Button variant="primary" className="flex-1">
    Continue
  </Button>
</Modal.Footer>

Single

Single full-width button.

<Modal.Footer layout="single">
  <Button variant="primary" className="w-full">
    Confirm
  </Button>
</Modal.Footer>

Right

Buttons aligned to the right with optional left addition.

<Modal.Footer layout="right">
  <Button variant="secondary" appearance="stroke">Cancel</Button>
  <Button variant="primary">Save</Button>
</Modal.Footer>

Footer Additions

Add extra elements to the footer left side (with layout="right").

Checkbox

<Modal.Footer
  layout="right"
  addition="checkbox"
  additionLabel="Don't show again"
  onAdditionChange={(checked) => console.log(checked)}
>
  <Button variant="secondary" appearance="stroke">Cancel</Button>
  <Button variant="primary">Save</Button>
</Modal.Footer>

Toggle

<Modal.Footer
  layout="right"
  addition="toggle"
  additionLabel="Enable notifications"
>
  <Button variant="secondary" appearance="stroke">Cancel</Button>
  <Button variant="primary">Save</Button>
</Modal.Footer>

Progress

<Modal.Footer
  layout="right"
  addition="progress"
  current={1}
  total={4}
>
  <Button variant="secondary" appearance="stroke">Back</Button>
  <Button variant="primary">Next</Button>
</Modal.Footer>

Extra Button

<Modal.Footer
  layout="right"
  addition="extraButton"
  extraButtonLabel="Reset"
  onExtraButtonClick={() => console.log('reset')}
>
  <Button variant="secondary" appearance="stroke">Cancel</Button>
  <Button variant="primary">Save</Button>
</Modal.Footer>

Controlled Mode

Control the modal open state externally.

const [open, setOpen] = useState(false)

<Modal open={open} onOpenChange={setOpen}>
  <Modal.Content>
    <Modal.Header title="Controlled Modal" description="..." />
    <Modal.Footer>
      <Button onClick={() => setOpen(false)}>Close</Button>
    </Modal.Footer>
  </Modal.Content>
</Modal>

<Button onClick={() => setOpen(true)}>Open Modal</Button>

Custom Body Content

Use Modal.Body for custom content between header and footer.

<Modal>
  <Modal.Content>
    <Modal.Header title="Form" description="Fill out the details below." />
    <Modal.Body>
      <form className="space-y-4">
        <input type="text" placeholder="Name" />
        <input type="email" placeholder="Email" />
      </form>
    </Modal.Body>
    <Modal.Footer>
      <Button variant="secondary" appearance="stroke">Cancel</Button>
      <Button variant="primary">Submit</Button>
    </Modal.Footer>
  </Modal.Content>
</Modal>

Granular Control

Build custom headers using individual components.

<Modal>
  <Modal.Content>
    <Modal.Header state="info" alignment="vertical">
      <Modal.Icon state="info" />
      <Modal.Title>Custom Header</Modal.Title>
      <Modal.Description>
        With completely custom content and structure.
      </Modal.Description>
    </Modal.Header>
    <Modal.Footer>
      <Button variant="primary">Got it</Button>
    </Modal.Footer>
  </Modal.Content>
</Modal>

Accessibility

  • Built on Radix UI Dialog for robust focus management
  • Proper ARIA attributes (role="dialog", aria-modal)
  • Focus trap within the modal
  • Auto-focus on first focusable element
  • Closes on Escape key press
  • Closes on overlay click
  • Screen reader friendly with title and description

API Reference

Modal / Modal.Root Props

PropTypeDefaultDescription
openboolean-Controlled open state
defaultOpenbooleanfalseDefault open state
onOpenChange(open: boolean) => void-Callback when open state changes
state'default' | 'error' | 'warning' | 'success' | 'info''default'Visual state variant
size'sm' | 'md' | 'lg''md'Modal size
alignment'horizontal' | 'vertical''horizontal'Header alignment

Modal.Content Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes

Modal.Header Props

PropTypeDefaultDescription
state'default' | 'error' | 'warning' | 'success' | 'info'Context valueVisual state variant
size'sm' | 'md' | 'lg'Context valueIcon size
alignment'horizontal' | 'vertical'Context valueLayout alignment
titlestring-Title text (shorthand)
descriptionstring-Description text (shorthand)
iconReact.ReactNode-Custom icon (overrides state icon)
showCloseButtonbooleanfalseShow close button
classNamestring-Additional CSS classes
childrenReact.ReactNode-Custom content (overrides title/description)

Modal.Body Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReact.ReactNode-Body content

Modal.Footer Props

PropTypeDefaultDescription
layout'double' | 'single' | 'right''double'Button layout
addition'none' | 'checkbox' | 'toggle' | 'progress' | 'extraButton''none'Left-side addition
additionLabelstring-Label for checkbox/toggle
additionCheckedboolean-Controlled checked state
additionDefaultCheckedboolean-Default checked state
onAdditionChange(checked: boolean) => void-Checkbox/toggle change handler
currentnumber0Current step for progress (0-indexed)
totalnumber3Total steps for progress
extraButtonLabelstring'Button'Extra button label
onExtraButtonClick() => void-Extra button click handler
classNamestring-Additional CSS classes

Modal.Title Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReact.ReactNode-Title content

Modal.Description Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReact.ReactNode-Description content

Modal.Icon Props

PropTypeDefaultDescription
state'default' | 'error' | 'warning' | 'success' | 'info'Context valueIcon state
size'sm' | 'md' | 'lg'Context valueIcon size
classNamestring-Additional CSS classes
childrenReact.ReactNode-Custom icon content

Modal.Close Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes
asChildbooleanfalseMerge props with child element

Modal.Trigger Props

PropTypeDefaultDescription
asChildbooleanfalseMerge props with child element