Dialog
A modal dialog component for displaying content in an overlay.
import { Button } from "@/components/ui/button/button";
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPopup,
DialogPortal,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog/dialog";
import { Input } from "@/components/ui/input/input";
import styles from "./dialog-demo.module.css";
export default function DialogDemo() {
return (
<Dialog>
<DialogTrigger render={<Button>Open Dialog</Button>} />
<DialogPortal>
<DialogOverlay />
<DialogPopup className={styles.popup}>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription className={styles.description}>Make changes to your profile here.</DialogDescription>
</DialogHeader>
<div className={styles.form}>
<div className={styles.fieldGroup}>
<label className={styles.label} htmlFor="name">
Name
</label>
<Input defaultValue="John Doe" id="name" />
</div>
<div className={styles.fieldGroup}>
<label className={styles.label} htmlFor="email">
Email
</label>
<Input defaultValue="john@example.com" id="email" type="email" />
</div>
</div>
<DialogFooter className={styles.footer}>
<DialogClose render={<Button variant="outline">Cancel</Button>} />
<Button>Save Changes</Button>
</DialogFooter>
</DialogPopup>
</DialogPortal>
</Dialog>
);
}
npx shadcn@latest add @roiui/dialognpx shadcn@latest add @roiui/dialog-tailwindanatomy
<Dialog>
<DialogTrigger />
<DialogContent>
<DialogHeader>
<DialogTitle />
<DialogDescription />
</DialogHeader>
<DialogFooter>
<DialogClose />
</DialogFooter>
</DialogContent>
</Dialog>
"use client";
import { X } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button/button";
import {
Dialog,
DialogClose,
DialogDescription,
DialogHeader,
DialogOverlay,
DialogPopup,
DialogPortal,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog/dialog";
import styles from "./dialog-sheet.module.css";
type Side = "left" | "right";
export default function DialogSheet() {
const [side, setSide] = useState<Side>("right");
return (
<div className={styles.buttons}>
<Dialog>
<DialogTrigger
render={
<Button onClick={() => setSide("left")} variant="outline">
Open Left
</Button>
}
/>
<DialogTrigger
render={
<Button onClick={() => setSide("right")} variant="outline">
Open Right
</Button>
}
/>
<DialogPortal>
<DialogOverlay />
<DialogPopup className={styles.popup} data-side={side}>
<DialogClose className={styles.closeButton} render={<button type="button" />}>
<X size={16} />
</DialogClose>
<DialogHeader className={styles.header}>
<DialogTitle>Sheet Panel</DialogTitle>
<DialogDescription>This dialog slides in from the {side} side of the screen.</DialogDescription>
</DialogHeader>
<div className={styles.content}>
<p>
Sheet dialogs are useful for navigation menus, settings panels, or any content that benefits from a
slide-in interaction.
</p>
</div>
<div className={styles.footer}>
<DialogClose render={<Button variant="outline">Close</Button>} />
<Button>Save</Button>
</div>
</DialogPopup>
</DialogPortal>
</Dialog>
</div>
);
}
"use client";
import { Dialog } from "@base-ui/react/dialog";
import { AnimatePresence, motion, type Variants } from "motion/react";
import { useRef, useState } from "react";
import styles from "./dialog-motion.module.css";
const contentVariants: Variants = {
hidden: {
opacity: 0,
x: 30,
y: 30,
scale: 0.7,
filter: "blur(4px)",
transformOrigin: "right center",
},
visible: {
opacity: 1,
transformOrigin: "right center",
scale: 1,
filter: "blur(0px)",
x: 0,
y: 0,
transition: {
duration: 0.38,
delay: 0.12,
type: "spring",
bounce: 0,
ease: "anticipate",
},
},
exit: {
opacity: 0,
filter: "blur(4px)",
scale: 0.97,
transition: {
duration: 0.5,
type: "spring",
},
},
};
const closeButtonVariants: Variants = {
hidden: {
opacity: 0,
filter: "blur(4px)",
scale: 0.97,
},
visible: {
opacity: 1,
scale: 1,
filter: "blur(0px)",
rotate: 0,
originX: -12,
transition: {
duration: 0.38,
delay: 0.12,
type: "spring",
bounce: 0,
ease: "anticipate",
},
},
exit: {
opacity: 0,
filter: "blur(4px)",
scale: 0.97,
transition: {
duration: 0.5,
type: "spring",
},
},
};
export default function DialogFramerMotion() {
const [open, setOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
return (
<Dialog.Root onOpenChange={setOpen} open={open}>
<div className={styles.triggerWrapper}>
<motion.div className={styles.morphWrapper} layoutId="wrapper" style={{ borderRadius: 8 }} />
<Dialog.Trigger className={styles.trigger} render={<motion.button layoutId="button" />}>
Upgrade
</Dialog.Trigger>
</div>
<AnimatePresence>
{open && (
<Dialog.Backdrop
className={styles.overlay}
hidden={undefined}
key="overlay"
render={
<motion.div
animate={{
opacity: 1,
}}
exit={{ opacity: 0 }}
initial={{ opacity: 0 }}
/>
}
/>
)}
</AnimatePresence>
<AnimatePresence>
{open && (
<Dialog.Portal container={containerRef} keepMounted>
<div className={styles.popupWrapper}>
<motion.div className={styles.popupMorphWrapper} layoutId="wrapper" style={{ borderRadius: 12 }} />
<Dialog.Popup className={styles.popup} hidden={undefined}>
<div className={styles.popupContent}>
<Dialog.Title
className={styles.title}
render={<motion.span animate="visible" exit="exit" initial="hidden" variants={contentVariants} />}
>
Plan Plus
</Dialog.Title>
<Dialog.Description
className={styles.description}
render={<motion.p animate="visible" exit="exit" initial="hidden" variants={contentVariants} />}
>
Upgrade your plan for full access.
</Dialog.Description>
<div className={styles.actions}>
<Dialog.Close
className={styles.closeButton}
render={
<motion.button
animate="visible"
exit="exit"
initial="hidden"
style={{ originX: -20, originY: -20 }}
variants={closeButtonVariants}
/>
}
>
Close
</Dialog.Close>
<motion.button className={styles.actionButton} layoutId="button">
Upgrade
</motion.button>
</div>
</div>
</Dialog.Popup>
</div>
</Dialog.Portal>
)}
</AnimatePresence>
</Dialog.Root>
);
}