The Dialog component extends Base UI's Dialog with polished default styles and structural subcomponents: Dialog.Header, Dialog.Body, and Dialog.Footer. Set size on Dialog.Root (sm, md, lg, xl) to pick a popup width preset. 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
"use client";
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
import clsx from "clsx";
import * as React from "react";
import styles from "./Dialog.module.scss";
export type DialogSize = "sm" | "md" | "lg" | "xl";
const DialogSizeContext = React.createContext<DialogSize>("md");
function Root({ size = "md", ...props }: Dialog.Root.Props) {
return (
<DialogSizeContext.Provider value={size}>
<BaseDialog.Root {...props} />
</DialogSizeContext.Provider>
);
}
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) {
const size = React.useContext(DialogSizeContext);
return (
<BaseDialog.Popup
className={clsx(
styles.popup,
size === "sm" && styles.popupSm,
size === "md" && styles.popupMd,
size === "lg" && styles.popupLg,
size === "xl" && styles.popupXl,
className,
)}
{...props}
/>
);
}
function Title({ className, ...props }: BaseDialog.Title.Props) {
return (
<BaseDialog.Title
className={clsx(styles.title, className)}
// biome-ignore lint/a11y/useHeadingContent: Base UI Title renders children into this heading
render={<h3 />}
{...props}
/>
);
}
function Description({ className, ...props }: BaseDialog.Description.Props) {
return (
<BaseDialog.Description
className={clsx(styles.description, className)}
{...props}
/>
);
}
function Header({ className, ...props }: Dialog.Header.Props) {
return <div className={clsx(styles.header, className)} {...props} />;
}
function Footer({ className, ...props }: Dialog.Footer.Props) {
return <div className={clsx(styles.footer, className)} {...props} />;
}
function Body({ className, ...props }: Dialog.Body.Props) {
return <div className={clsx(styles.body, className)} {...props} />;
}
// biome-ignore lint/correctness/noUnusedVariables: merged with `namespace Dialog` for compound export
const Dialog = {
Root,
createHandle,
Trigger,
Portal,
Close,
Backdrop,
Popup,
Header,
Footer,
Title,
Description,
Body,
};
namespace Dialog {
export namespace Root {
export interface Props extends BaseDialog.Root.Props {
size?: DialogSize;
}
}
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 the size prop on Dialog.Root to choose a popup width. The default is md.
Use open and onOpenChange to control the dialog programmatically without a Dialog.Trigger.