The Dialog component extends Base UI's Dialog with polished default styles and structural subcomponents: Dialog.Header, Dialog.Body, and Dialog.Footer. Unlike AlertDialog (which uses role="alertdialog" for confirmations), Dialog uses role="dialog" and closes on outside click or Escape by default.
Here is the component implementation code that you can copy to your project:
// https://devie-ui.com/components/dialog
// https://base-ui.com/react/components/dialog
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
import clsx from "clsx";
import type React from "react";
import styles from "./Dialog.module.scss";
const Root = BaseDialog.Root;
const createHandle = BaseDialog.createHandle;
function Trigger({ className, ...props }: BaseDialog.Trigger.Props) {
return (
<BaseDialog.Trigger
className={clsx(styles.trigger, className)}
{...props}
/>
);
}
const Portal = BaseDialog.Portal;
function Close({ className, render, ...props }: BaseDialog.Close.Props) {
return (
<BaseDialog.Close
className={clsx(!render && styles.close, className)}
render={render}
{...props}
/>
);
}
function Backdrop({ className, ...props }: BaseDialog.Backdrop.Props) {
return (
<BaseDialog.Backdrop
className={clsx(styles.backdrop, className)}
{...props}
/>
);
}
function Popup({ className, ...props }: BaseDialog.Popup.Props) {
return (
<BaseDialog.Popup
className={clsx(styles.popup, className)}
{...props}
/>
);
}
function Title({ className, ...props }: BaseDialog.Title.Props) {
return (
<BaseDialog.Title
className={clsx(styles.title, className)}
render={({ children }) => (
<h3 className={clsx(styles.title, className)} {...props}>
{children}
</h3>
)}
{...props}
/>
);
}
function Description({ className, ...props }: BaseDialog.Description.Props) {
return (
<BaseDialog.Description
className={clsx(styles.description, className)}
{...props}
/>
);
}
interface HeaderProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
function Header({ className, ...props }: HeaderProps) {
return <div className={clsx(styles.header, className)} {...props} />;
}
interface FooterProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
function Footer({ className, ...props }: FooterProps) {
return <div className={clsx(styles.footer, className)} {...props} />;
}
interface BodyProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
function Body({ className, ...props }: BodyProps) {
return <div className={clsx(styles.body, className)} {...props} />;
}
const Dialog = {
Root,
createHandle,
Trigger,
Portal,
Close,
Backdrop,
Popup,
Header,
Footer,
Title,
Description,
Body,
};
namespace Dialog {
export namespace Root {
export type Props = BaseDialog.Root.Props;
}
export namespace Trigger {
export type Props = BaseDialog.Trigger.Props;
}
export namespace Portal {
export type Props = BaseDialog.Portal.Props;
}
export namespace Close {
export type Props = BaseDialog.Close.Props;
}
export namespace Backdrop {
export type Props = BaseDialog.Backdrop.Props;
}
export namespace Popup {
export type Props = BaseDialog.Popup.Props;
}
export namespace Title {
export type Props = BaseDialog.Title.Props;
}
export namespace Description {
export type Props = BaseDialog.Description.Props;
}
export namespace Header {
export interface Props extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
}
export namespace Footer {
export interface Props extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
}
export namespace Body {
export interface Props extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
}
}
export default Dialog;A basic dialog with a title, description, and action buttons. Use Dialog.Trigger to open and Dialog.Close to dismiss.
Use open and onOpenChange to control the dialog programmatically without a Dialog.Trigger.