Overview
The Dialog component is a modal overlay built on Radix UI that displays content in a layer above the main application. It includes composable parts for header, footer, title, description, and close functionality.
Import
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose
} from '@repo/ui/dialog'
Usage
Basic Dialog
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
export default function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="filled">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>
This is a description of the dialog content.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)
}
Confirmation Dialog
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose
} from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
export default function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete Account</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. Your organization and associated
records will be deleted.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Components
Dialog
The root dialog component that manages open/close state.
Controlled open state of the dialog.
Callback fired when the open state changes.
Default open state for uncontrolled usage.
Whether the dialog should be modal.
DialogTrigger
Button or element that opens the dialog when clicked.
When true, renders the trigger as a Slot component for composition.
<DialogTrigger asChild>
<Button>Open</Button>
</DialogTrigger>
DialogContent
The main dialog content container with overlay.
When true, displays the close button in the top-right corner.
Additional CSS classes to apply to the content container.
Container for the dialog title and description.
Additional CSS classes to apply to the header.
DialogTitle
The title of the dialog.
Additional CSS classes to apply to the title.
DialogDescription
Description text displayed below the title.
Additional CSS classes to apply to the description.
Container for action buttons at the bottom of the dialog.
Additional CSS classes to apply to the footer.
DialogClose
Button or element that closes the dialog when clicked.
When true, renders as a Slot component for composition.
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
DialogOverlay
The overlay backdrop behind the dialog content.
Additional CSS classes to apply to the overlay.
DialogPortal
Portal component that renders dialog content in a portal.
Examples
Controlled Dialog
import { useState } from 'react'
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle } from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
export default function Example() {
const [open, setOpen] = useState(false)
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Controlled Dialog</DialogTitle>
</DialogHeader>
<Button onClick={() => setOpen(false)}>
Close Programmatically
</Button>
</DialogContent>
</Dialog>
)
}
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
import { Input } from '@repo/ui/input'
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@repo/ui/form'
import { useForm } from 'react-hook-form'
export default function Example() {
const form = useForm()
const onSubmit = (data) => {
console.log(data)
}
return (
<Dialog>
<DialogTrigger asChild>
<Button>Create Item</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Create New Item</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} placeholder="Item name" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button type="submit">Create</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
)
}
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose } from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
export default function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Important Action</Button>
</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Confirm Action</DialogTitle>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
<Button variant="filled">Proceed</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Nested Content
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@repo/ui/dialog'
import { Button } from '@repo/ui/button'
export default function Example() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="filled">Confirm Action</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirm Your Action</DialogTitle>
<DialogDescription>
Please confirm that you want to proceed with this action.
This action is irreversible.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<p className="text-sm text-muted-foreground">
Additional content can go here.
</p>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
<Button variant="filled">Proceed</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Features
- Modal Overlay: Blocks interaction with the rest of the application
- Focus Management: Automatically traps and manages focus
- Close on Outside Click: Closes when clicking the overlay
- Close on Escape: Closes when pressing the Escape key
- Composable: Flexible composition of header, body, and footer
- Accessible: Built with proper ARIA attributes
Accessibility
- Focuses the first focusable element on open
- Traps focus within the dialog
- Returns focus to trigger on close
- Closes on Escape key
- Screen reader accessible with proper ARIA roles
- Close button has screen reader text “Close”
Styling
The dialog content includes:
- Centered positioning with responsive sizing
- Smooth fade and scale animation
- Semi-transparent overlay backdrop
- Close button positioned in top-right corner with X icon
Source
View source: packages/ui/src/dialog/dialog.tsx