Dialog.tsx
Modal dialog component built with Radix UI for accessible overlays
The Dialog component is a fully accessible modal dialog built on Radix UI primitives with animations and customizable content.
Features
- Accessible modal with focus management
- Backdrop overlay with blur effect
- Smooth open/close animations
- Optional close button
- Keyboard navigation (Esc to close)
- Portal rendering for proper stacking
Basic Usage
'use client';
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/Dialog';
import { Button } from '@/components/ui/Button';
export default function Page() {
return (
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>
This is a description of the dialog content.
</DialogDescription>
</DialogHeader>
<p>Dialog content goes here.</p>
</DialogContent>
</Dialog>
);
}With Footer Actions
'use client';
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/Dialog';
import { Button } from '@/components/ui/Button';
<Dialog>
<DialogTrigger asChild>
<Button>Delete Account</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
</DialogHeader>
<p>This action cannot be undone.</p>
<DialogFooter>
<Button variant="outline">Cancel</Button>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>Without Close Button
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>No Close Button</DialogTitle>
</DialogHeader>
<p>User must click an action button to close.</p>
<DialogFooter>
<Button>Close</Button>
</DialogFooter>
</DialogContent>Controlled Dialog
'use client';
import { useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/Dialog';
import { Button } from '@/components/ui/Button';
export default function Page() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Open</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Controlled Dialog</DialogTitle>
</DialogHeader>
<p>This dialog is controlled by state.</p>
<Button onClick={() => setOpen(false)}>Close</Button>
</DialogContent>
</Dialog>
</>
);
}Custom Styling
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle className="text-2xl">Large Dialog</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<p>Custom styled dialog with larger width.</p>
</div>
</DialogContent>With Form
'use client';
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/Dialog';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Label } from '@/components/ui/Label';
<Dialog>
<DialogTrigger asChild>
<Button>Edit Profile</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
</DialogHeader>
<form className="space-y-4">
<div>
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Enter your name" />
</div>
<div>
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="Enter your email" />
</div>
<DialogFooter>
<Button type="submit">Save Changes</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>Implementation
The Dialog is built with Radix UI primitives:
'use client';
import * as DialogPrimitive from '@radix-ui/react-dialog';
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root {...props} />;
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean;
}) {
return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
className={cn(
'bg-glass-surface backdrop-blur-lg fixed top-[50%] left-[50%] z-50 translate-x-[-50%] translate-y-[-50%] rounded-lg border p-6',
className
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close className="absolute top-4 right-4">
<XIcon />
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
);
}Components
Dialog
Root component that manages dialog state.
DialogTrigger
Button or element that opens the dialog.
DialogContent
Main dialog content container with backdrop and animations.
DialogHeader
Header section for title and description.
DialogTitle
Dialog title (required for accessibility).
DialogDescription
Optional description text.
DialogFooter
Footer section for action buttons.
DialogClose
Programmatic close trigger.
Accessibility
- Focus trapped within dialog when open
- Esc key closes dialog
- Click outside closes dialog
- Focus returns to trigger on close
- Proper ARIA attributes
- Screen reader announcements
Animations
- Fade in/out for overlay
- Zoom in/out for content
- Smooth transitions with Tailwind animations
How is this guide ?
Last updated on