React dialog component for modal windows with focus trapping and keyboard navigation. Built with TypeScript and Tailwind CSS for Next.js using Radix UI.
Ever built a form where users accidentally clicked outside and lost all their data? Or watched someone frantically click "Delete" without reading the confirmation? Yeah, modals without proper focus management are UX disasters waiting to happen. This shadcn/ui dialog brings real modal behavior to your React apps.
Built on Radix UI's Dialog primitive with smooth animations and proper accessibility. Styled with Tailwind CSS so it matches your design system instead of looking like a Bootstrap modal from 2015.
Here's the thing—dialogs aren't just popup divs. They're focus management tools that force users to make decisions. Done wrong, they're annoying interruptions. Done right, they prevent costly mistakes and guide users through critical workflows.
Think about how GitHub handles repository deletion or how Stripe confirms payment changes. You can't accidentally click through. The dialog demands attention, gets confirmation, then gets out of the way. No accidental deletions, no lost form data.
This free shadcn dialog handles the complex parts—focus trapping, scroll locking, keyboard navigation, screen reader announcements—while you focus on the content. Whether you're building confirmation flows, login forms, or settings panels in your Next.js applications, dialogs that respect user attention make everything feel more professional in your JavaScript projects.
The classic "Are you sure?" for destructive actions:
Loading component...
"use client"import { Button } from "~/components/ui/button"import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger,} from "~/components/ui/dialog"export default function DialogSimple() { return ( <div className="w-full p-6 flex justify-center"> <Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Are you absolutely sure?</DialogTitle> <DialogDescription> This action cannot be undone. This will permanently delete your account and remove your data from our servers. </DialogDescription> </DialogHeader> </DialogContent> </Dialog> </div> )}
"use client"import { Button } from "~/components/ui/button"import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "~/components/ui/dialog"import { ScrollArea } from "~/components/ui/scroll-area"export default function DialogScrollable() { return ( <div className="w-full p-6 flex justify-center"> <Dialog> <DialogTrigger asChild> <Button variant="outline">View Terms</Button> </DialogTrigger> <DialogContent className="max-h-[80vh]"> <DialogHeader> <DialogTitle>Terms of Service</DialogTitle> <DialogDescription>Please read our terms of service carefully.</DialogDescription> </DialogHeader> <ScrollArea className="h-[400px] w-full rounded-md border p-4"> <div className="space-y-4"> <div> <h4 className="font-medium">1. Acceptance of Terms</h4> <p className="text-sm text-muted-foreground"> By accessing and using this service, you accept and agree to be bound by the terms and provision of this agreement. If you do not agree to abide by the above, please do not use this service. </p> </div> <div> <h4 className="font-medium">2. Use License</h4> <p className="text-sm text-muted-foreground"> Permission is granted to temporarily download one copy of the materials (information or software) on our service for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title. </p> </div> <div> <h4 className="font-medium">3. Disclaimer</h4> <p className="text-sm text-muted-foreground"> The materials on our service are provided on an 'as is' basis. We make no warranties, expressed or implied, and hereby disclaim and negate all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights. </p> </div> <div> <h4 className="font-medium">4. Limitations</h4> <p className="text-sm text-muted-foreground"> In no event shall our company or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on our service, even if we or our authorized representative has been notified orally or in writing of the possibility of such damage. </p> </div> <div> <h4 className="font-medium">5. Privacy Policy</h4> <p className="text-sm text-muted-foreground"> Your privacy is important to us. Our privacy policy explains how we collect, use, and protect your information when you use our service. By using our service, you agree to the collection and use of information in accordance with our privacy policy. </p> </div> <div> <h4 className="font-medium">6. Governing Law</h4> <p className="text-sm text-muted-foreground"> These terms and conditions are governed by and construed in accordance with the laws of the country in which the company is registered and you irrevocably submit to the exclusive jurisdiction of the courts in that State or location. </p> </div> </div> </ScrollArea> <DialogFooter> <Button type="submit">Accept Terms</Button> </DialogFooter> </DialogContent> </Dialog> </div> )}
Write clear action labels. This free shadcn/ui dialog works perfectly, but users need to understand what clicking buttons does. "Delete Account" beats "OK". "Save Changes" beats "Submit". Your React component shows the dialog—you provide the clarity that prevents mistakes.
Give multiple escape routes. Users panic when they feel trapped. Include the X button, Cancel button, Escape key, and click-outside-to-close. This TypeScript component supports all these patterns—use them all in your Next.js applications.
Focus the right element. When the dialog opens, focus should land on the first interactive element or the most likely action. This open source shadcn component manages focus—you decide where it goes based on user intent.
Handle loading states. Show spinners or disable buttons during async operations. Users will click "Save" multiple times if nothing happens. Your JavaScript dialog should give immediate feedback, even if the operation takes time.
Keep dialogs focused. One dialog, one purpose. Don't cram account settings, profile editing, and password changes into one modal. This Tailwind CSS component handles any content, but focused dialogs convert better than kitchen sink modals.
Dialogs naturally work with Form components for data collection and validation in your React applications. Use Button components for consistent action styling across all your dialogs.
For complex workflows, combine dialogs with Tabs components to organize multi-step processes. Alert components work well inside dialogs for showing validation errors or important notices. This open source pattern keeps your modals consistent and accessible.
When building confirmation flows, pair dialogs with AlertDialog components for critical actions that need extra attention. ScrollArea components handle long content like terms of service elegantly. Your JavaScript application can compose these components while maintaining consistent behavior.
For loading states, use dialogs with Skeleton components or Spinner patterns to show progress. The dialog provides the container—other shadcn components handle specific UI states.