Skip to Content

Modal

Overlay dialog component for focused user interactions.

Import

import { Modal } from 'poodle'

Usage

Basic Modal

function BasicModal() { const [open, setOpen] = useState(false) return ( <> <Button onClick={() => setOpen(true)}>Open Modal</Button> <Modal open={open} onClose={() => setOpen(false)}> <Modal.Header> <h2>Modal Title</h2> </Modal.Header> <Modal.Body> <p>This is the modal content.</p> </Modal.Body> <Modal.Footer> <Button variant="outline" onClick={() => setOpen(false)}> Cancel </Button> <Button onClick={() => setOpen(false)}> Confirm </Button> </Modal.Footer> </Modal> </> ) }

Confirmation Modal

function ConfirmModal() { const [open, setOpen] = useState(false) const handleDelete = () => { // Perform delete action setOpen(false) } return ( <> <Button variant="danger" onClick={() => setOpen(true)}> Delete Item </Button> <Modal open={open} onClose={() => setOpen(false)}> <Modal.Header> <h2>Confirm Deletion</h2> </Modal.Header> <Modal.Body> <p>Are you sure you want to delete this item? This action cannot be undone.</p> </Modal.Body> <Modal.Footer> <Button variant="outline" onClick={() => setOpen(false)}> Cancel </Button> <Button variant="danger" onClick={handleDelete}> Delete </Button> </Modal.Footer> </Modal> </> ) }

Form Modal

function FormModal() { const [open, setOpen] = useState(false) const [formData, setFormData] = useState({ name: '', email: '' }) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() // Handle form submission setOpen(false) } return ( <> <Button onClick={() => setOpen(true)}>Add User</Button> <Modal open={open} onClose={() => setOpen(false)}> <form onSubmit={handleSubmit}> <Modal.Header> <h2>Add New User</h2> </Modal.Header> <Modal.Body> <div className="space-y-4"> <Input label="Name" value={formData.name} onChange={(e) => setFormData({...formData, name: e.target.value})} required /> <Input type="email" label="Email" value={formData.email} onChange={(e) => setFormData({...formData, email: e.target.value})} required /> </div> </Modal.Body> <Modal.Footer> <Button variant="outline" type="button" onClick={() => setOpen(false)}> Cancel </Button> <Button type="submit"> Add User </Button> </Modal.Footer> </form> </Modal> </> ) }
// Small Modal <Modal size="sm" open={open} onClose={onClose}> {/* content */} </Modal> // Medium Modal (default) <Modal size="md" open={open} onClose={onClose}> {/* content */} </Modal> // Large Modal <Modal size="lg" open={open} onClose={onClose}> {/* content */} </Modal> // Full Screen Modal <Modal size="full" open={open} onClose={onClose}> {/* content */} </Modal>

API Reference

PropTypeDefaultDescription
openbooleanfalseWhether the modal is visible
onClose() => voidundefinedCallback when modal should close
size'sm' | 'md' | 'lg' | 'full''md'Modal size
closeOnOverlayClickbooleantrueClose modal when clicking overlay
closeOnEscapebooleantrueClose modal when pressing Escape
childrenReactNodeundefinedModal content

Examples

Alert Modal

function AlertModal({ message }: { message: string }) { const [open, setOpen] = useState(false) return ( <Modal open={open} onClose={() => setOpen(false)} size="sm"> <Modal.Body> <div className="text-center py-4"> <div className="text-4xl mb-4">⚠️</div> <p className="text-lg">{message}</p> </div> </Modal.Body> <Modal.Footer> <Button onClick={() => setOpen(false)} className="w-full"> OK </Button> </Modal.Footer> </Modal> ) }

Loading Modal

function LoadingModal() { const [loading, setLoading] = useState(false) return ( <Modal open={loading} closeOnOverlayClick={false} closeOnEscape={false}> <Modal.Body> <div className="text-center py-8"> <Spinner size="lg" /> <p className="mt-4">Loading...</p> </div> </Modal.Body> </Modal> ) }

Multi-step Modal

function MultiStepModal() { const [open, setOpen] = useState(false) const [step, setStep] = useState(1) return ( <Modal open={open} onClose={() => setOpen(false)}> <Modal.Header> <h2>Step {step} of 3</h2> </Modal.Header> <Modal.Body> {step === 1 && <Step1Content />} {step === 2 && <Step2Content />} {step === 3 && <Step3Content />} </Modal.Body> <Modal.Footer> {step > 1 && ( <Button variant="outline" onClick={() => setStep(step - 1)}> Back </Button> )} {step < 3 ? ( <Button onClick={() => setStep(step + 1)}> Next </Button> ) : ( <Button onClick={() => setOpen(false)}> Finish </Button> )} </Modal.Footer> </Modal> ) }

Accessibility

  • Traps focus within modal
  • Closes on Escape key (configurable)
  • Prevents body scroll when open
  • ARIA attributes for screen readers
  • Focus returns to trigger element on close

Best Practices

Do ✅

  • Use modals for important actions requiring attention
  • Keep modal content focused and concise
  • Provide clear close/cancel options
  • Use appropriate modal sizes

Don’t ❌

  • Don’t nest modals
  • Don’t use modals for non-critical information
  • Don’t make modals too complex
  • Don’t forget keyboard accessibility
  • Button - Modal triggers and actions
  • Card - Content containers
  • Input - Form inputs in modals