Modal
A dialog window overlaid on the primary content for critical information or actions.
Introduction
Modals display focused content that requires user attention or interaction. Built on Radix UI Dialog for accessibility, they support multiple sizes and include header, body, and footer sections.
Installation
import {
Modal, ModalTrigger, ModalContent, ModalHeader,
ModalTitle, ModalBody, ModalFooter, ModalClose,
} from '@/components/ui/modal';Usage
<Modal>
<ModalTrigger asChild>
<Button>Open Modal</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader><ModalTitle>Title</ModalTitle></ModalHeader>
<ModalBody>Content here</ModalBody>
<ModalFooter>
<ModalClose asChild><Button variant="outlined">Cancel</Button></ModalClose>
<Button>Confirm</Button>
</ModalFooter>
</ModalContent>
</Modal>Examples
Default
import { Button } from '@/components/ui/button';
import { Modal, ModalTrigger, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter, ModalClose } from '@/components/ui/modal';
export default function Component() {
return (
<Modal>
<ModalTrigger asChild><Button>Open Modal</Button></ModalTrigger>
<ModalContent>
<ModalHeader><ModalTitle>Modal Title</ModalTitle></ModalHeader>
<ModalBody><p className="text-sm">Modal body content.</p></ModalBody>
<ModalFooter>
<ModalClose asChild><Button variant="outlined">Cancel</Button></ModalClose>
<Button>Save Changes</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}Sizes
Use the size prop on ModalContent to change the dialog width. Available sizes: sm (300px), md (500px), lg (800px), xl (1140px), fullscreen.
import { Button } from '@/components/ui/button';
import { Modal, ModalTrigger, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter, ModalClose } from '@/components/ui/modal';
export default function Component() {
return (
<div className="flex flex-wrap gap-2">
{(['sm', 'md', 'lg'] as const).map((size) => (
<Modal key={size}>
<ModalTrigger asChild><Button variant="outlined">{size.toUpperCase()}</Button></ModalTrigger>
<ModalContent size={size}>
<ModalHeader><ModalTitle>{size.toUpperCase()} Modal</ModalTitle></ModalHeader>
<ModalBody><p className="text-sm">This is a {size} sized modal.</p></ModalBody>
<ModalFooter><ModalClose asChild><Button>Close</Button></ModalClose></ModalFooter>
</ModalContent>
</Modal>
))}
</div>
);
}Scrollable
Use the scrollable prop on ModalContent to make the modal body scrollable when content overflows, keeping the header and footer fixed.
'use client';
/* eslint-disable eslint-frontend-rules/enforce-typography-components */
import { Button } from '@/components/ui/button';
import {
Modal,
ModalBody,
ModalClose,
ModalContent,
ModalFooter,
ModalHeader,
ModalTitle,
ModalTrigger,
} from '@/components/ui/modal';
export function ModalDefault() {
return (
<Modal>
<ModalTrigger asChild>
<Button>Open Modal</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>Modal Title</ModalTitle>
</ModalHeader>
<ModalBody>
<p className='text-sm'>
This is the modal body content. You can place any content here including forms, text,
images, and other components.
</p>
</ModalBody>
<ModalFooter>
<ModalClose asChild>
<Button variant='outlined'>Cancel</Button>
</ModalClose>
<Button>Save Changes</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
export function ModalSizes() {
return (
<div className='flex flex-wrap gap-2'>
{(['sm', 'md', 'lg', 'xl'] as const).map(size => (
<Modal key={size}>
<ModalTrigger asChild>
<Button variant='outlined'>{size.toUpperCase()}</Button>
</ModalTrigger>
<ModalContent size={size}>
<ModalHeader>
<ModalTitle>{size.toUpperCase()} Modal</ModalTitle>
</ModalHeader>
<ModalBody>
<p className='text-sm'>
This is a {size} sized modal (
{{ sm: '300px', md: '500px', lg: '800px', xl: '1140px' }[size]}).
</p>
</ModalBody>
<ModalFooter>
<ModalClose asChild>
<Button>Close</Button>
</ModalClose>
</ModalFooter>
</ModalContent>
</Modal>
))}
</div>
);
}
export function ModalScrollable() {
return (
<Modal>
<ModalTrigger asChild>
<Button variant='outlined'>Scrollable Modal</Button>
</ModalTrigger>
<ModalContent scrollable>
<ModalHeader>
<ModalTitle>Scrollable Content</ModalTitle>
</ModalHeader>
<ModalBody scrollable>
<div className='space-y-4'>
{Array.from({ length: 10 }, (_, i) => (
<p key={i} className='text-sm'>
This is paragraph {i + 1} of the scrollable content. When the content exceeds the
modal height, the body becomes scrollable while the header and footer remain fixed.
</p>
))}
</div>
</ModalBody>
<ModalFooter>
<ModalClose asChild>
<Button>Close</Button>
</ModalClose>
</ModalFooter>
</ModalContent>
</Modal>
);
}
Note: UX4G removes header and footer borders from the modal for a cleaner appearance.
API Reference
ModalContent
| Prop | Type | Default | Description |
|---|---|---|---|
size | sm | md | lg | xl | fullscreen | md | Dialog width (sm=300px, md=500px, lg=800px, xl=1140px) |
scrollable | boolean | false | Enable scrollable modal body |