Dialog
A modal dialog component built on the native HTML dialog element
Installation
Usage
Basic Usage
A basic dialog component in controlled mode, with built-in Tab focus trapping and Esc close handling.
"use client";
import { useState } from "react";
import { Button } from "@/ui";
import { Dialog } from "@/ui";
export default function Demo() {
const [open, setOpen] = useState(false);
const handleBackdropClick = () => {
setOpen(false);
};
return (
<div className="flex flex-col items-center gap-4">
<Button onClick={() => setOpen(true)}>Open Dialog</Button>
<Dialog open={open} onOpenChange={setOpen} onBackdropClick={handleBackdropClick}>
<div className="flex flex-col gap-6 pt-4">
<div className="flex flex-col gap-2">
<h3 className="text-lg font-semibold">Welcome to Litefy</h3>
<p className="text-sm text-muted-foreground">
A beautiful, fast, and modern React UI library for building accessible and
customizable web applications with ease.
</p>
</div>
<Button className="w-full" onClick={() => setOpen(false)}>
Continue
</Button>
</div>
</Dialog>
</div>
);
}
Custom
Assemble the composable parts with your own open/close lifecycle. The focus trap lives in the composite Dialog and is not part of the atoms.
"use client";
import { useState, useRef, useEffect } from "react";
import { DialogRoot, DialogClose, DialogContent } from "@/ui";
export default function Demo() {
const [open, setOpen] = useState(false);
const dialogRef = useRef<HTMLDialogElement>(null);
useEffect(() => {
const dialog = dialogRef.current;
if (!dialog) return;
if (open) {
if (!dialog.open) dialog.showModal();
} else {
if (dialog.open) dialog.close();
}
}, [open]);
return (
<div className="flex flex-col items-center gap-4">
<button onClick={() => setOpen(true)}>Open Dialog</button>
<DialogRoot
ref={dialogRef}
onCancel={(e) => {
e.preventDefault();
setOpen(false);
}}
onClose={() => setOpen(false)}
onClick={(e) => {
if (e.target === e.currentTarget) setOpen(false);
}}
className="backdrop:bg-muted/50"
>
<DialogClose aria-label="Close (ESC)" onClick={() => setOpen(false)}>
ESC
</DialogClose>
<DialogContent className="flex flex-col gap-6 pt-4">
<div className="flex flex-col gap-2">
<h3 className="text-lg font-semibold">Assembled from parts</h3>
<p className="text-sm text-muted-foreground">
A plain <code>DialogRoot</code> plus a <code>DialogClose</code> and a{" "}
<code>DialogContent</code>, with your own open/close lifecycle.
</p>
</div>
<button
className="w-full rounded-md border py-2 text-sm font-medium hover:bg-muted"
onClick={() => setOpen(false)}
>
Continue
</button>
</DialogContent>
</DialogRoot>
</div>
);
}
Command Dialog
A dialog triggered imperatively via the dialog.success / dialog.error / dialog.warning / dialog.info methods, created dynamically and destroyed automatically after closing.
"use client";
import { Button } from "@/ui";
import { dialog } from "@/ui";
import { CircleAlert, CircleCheck, Info, TriangleAlert } from "lucide-react";
export default function Demo() {
return (
<div className="flex flex-col items-center gap-4">
<Button
onClick={() => {
dialog.success({
title: <span className="flex gap-2"><CircleCheck className="fill-success text-background"/>Success</span>,
children: "Operation completed successfully.",
});
}}
>
Open Success Dialog
</Button>
<Button
onClick={() => {
dialog.warning({
title: <span className="flex gap-2"><TriangleAlert className="fill-warning text-background"/>Warning</span>,
children: "Please check your input before submitting.",
});
}}
>
Open Warning Dialog
</Button>
<Button
onClick={() => {
dialog.error({
title: <span className="flex gap-2"><CircleAlert className="fill-danger text-background"/>Error</span>,
children: "Something went wrong, please try again later.",
});
}}
>
Open Error Dialog
</Button>
<Button
onClick={() => {
dialog.info({
title: <span className="flex gap-2"><Info className="fill-info text-background"/>Info</span>,
children: "This is an informational message.",
});
}}
>
Open Info Dialog
</Button>
</div>
);
}
API Reference
High-level Components
Ready-to-use composite components.
Dialog
A modal dialog component built on the native HTML dialog element, with built-in focus trapping and Esc close handling.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - (required) | Controlled open state, synced with the native showModal() / close() internally |
onOpenChange | (open: boolean) => void | - | Callback when the open state changes; fired on Esc, close button click, native cancel / close |
onBackdropClick | (e: React.MouseEvent<HTMLDialogElement>) => void | - | Callback fired when the backdrop is clicked; implement close-on-backdrop-click with it |
classNames | { root?, content?, close? } | - | Custom class names per part, applied to the dialog element, content area and close button |
styles | { root?, content?, close? } | - | Inline styles per part |
children | React.ReactNode | - | Dialog content, rendered inside the content area DialogContent |
...props | React.ComponentProps<"div"> | - | Remaining props are passed through to the content area DialogContent (except className / styles) |
Composable Components
DialogRoot
The dialog element, rendered as a plain native dialog with no built-in styles or behavior.
| Prop | Type | Default | Description |
|---|---|---|---|
className | ClassNameValue | - | Custom class name, applied to the native dialog element |
...props | React.ComponentProps<"dialog"> | - | Supports all native dialog props (except className) |
DialogClose
Close button with built-in styles, absolutely positioned in the top-right corner of the nearest positioned ancestor (i.e. the dialog element).
| Prop | Type | Default | Description |
|---|---|---|---|
className | ClassNameValue | - | Custom class name |
children | React.ReactNode | - | Button content |
...props | React.ComponentProps<"button"> | - | Supports all native button props (except className) |
DialogContent
Content container with built-in default panel styles: centered positioning, min-w-70 max-w-md width, rounded border, shadow and background.
| Prop | Type | Default | Description |
|---|---|---|---|
className | ClassNameValue | - | Custom class name |
children | React.ReactNode | - | Content area content |
...props | React.ComponentProps<"div"> | - | Supports all native div props (except className) |
dialog
Imperative trigger methods that create the dialog dynamically and destroy it automatically after closing.
| Method | Type | Description |
|---|---|---|
success | (opts: DialogCommandOptions) => void | Opens a command dialog |
error | (opts: DialogCommandOptions) => void | Opens a command dialog |
warning | (opts: DialogCommandOptions) => void | Opens a command dialog |
info | (opts: DialogCommandOptions) => void | Opens a command dialog |
The four methods are semantic shortcuts with identical behavior. Browser limitation: only one modal dialog (
showModal()) can be open at a time; repeated opens are blocked by the browser rather than queued.
DialogCommandOptions
| Field | Type | Description |
|---|---|---|
title | React.ReactNode | Dialog title |
children | React.ReactNode | Dialog content |
props | DialogProps | Pass-through props forwarded to Dialog |