IndiaCN UI

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

ModalDefault
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.

ModalSizes
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.

ModalScrollable
'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

PropTypeDefaultDescription
sizesm | md | lg | xl | fullscreenmdDialog width (sm=300px, md=500px, lg=800px, xl=1140px)
scrollablebooleanfalseEnable scrollable modal body

On this page