Dialog
A window overlaid on the page, rendering the content underneath inert.
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
export default function DialogDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Open dialog
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete project</DialogTitle>
<DialogDescription>
This action cannot be undone. The project and all of its data
will be permanently removed.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
<DialogClose render={<Button variant="destructive" />}>
Delete
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
Install
npx shadcn@latest add @madeui/dialog
Usage
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
Composition
<Dialog>
<DialogTrigger />
<DialogContent>
<DialogHeader>
<DialogTitle />
<DialogDescription />
</DialogHeader>
<DialogFooter>
<DialogClose />
</DialogFooter>
</DialogContent>
</Dialog>
Custom width
Override the content width with the style prop.
import * as stylex from '@stylexjs/stylex';
import { container } from '@/lib/constants.stylex';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
export default function DialogCustom() {
return (
<Dialog>
<DialogTrigger render={<Button />}>Rename</DialogTrigger>
<DialogContent style={styles.narrow}>
<DialogHeader>
<DialogTitle>Rename project</DialogTitle>
<DialogDescription>Give your project a new name.</DialogDescription>
</DialogHeader>
<Input defaultValue="madeui" />
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
<DialogClose render={<Button />}>Save</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
const styles = stylex.create({
narrow: {
width: container.md,
},
});
Scrollable content
Give DialogContent a maxHeight and wrap the long content in its own
scrollable container — the header and footer stay put.
import * as stylex from '@stylexjs/stylex';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import { space } from '@/lib/constants.stylex';
export default function DialogScrollable() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
View terms
</DialogTrigger>
<DialogContent style={styles.content}>
<DialogHeader>
<DialogTitle>Terms of Service</DialogTitle>
<DialogDescription>
Last updated February 2026. Please read before continuing.
</DialogDescription>
</DialogHeader>
<div {...stylex.props(styles.body)}>
<p>
1. Acceptance of terms. By accessing or using this service, you
agree to be bound by these terms and all applicable laws and
regulations.
</p>
<p>
2. Use license. Permission is granted to temporarily use this
service for personal, non-commercial purposes only. This is the
grant of a license, not a transfer of title.
</p>
<p>
3. Account responsibilities. You are responsible for maintaining
the confidentiality of your account credentials and for all
activity that occurs under your account.
</p>
<p>
4. Service availability. We do not guarantee that the service
will be uninterrupted, timely, secure, or error-free, and we
reserve the right to modify or discontinue it at any time.
</p>
<p>
5. Limitation of liability. In no event shall we be liable for
any indirect, incidental, special, or consequential damages
arising out of your use of the service.
</p>
<p>
6. Changes to terms. We may revise these terms at any time. By
continuing to use the service after changes take effect, you
agree to the revised terms.
</p>
<p>
7. Termination. We may suspend or terminate your access at any
time, without notice, for conduct that violates these terms.
</p>
</div>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Decline</DialogClose>
<DialogClose render={<Button />}>Accept</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
const styles = stylex.create({
content: {
maxHeight: `calc(100dvh - ${space.s16})`,
},
body: {
display: 'flex',
flexDirection: 'column',
flex: 1,
gap: space.s3,
minHeight: 0,
overflowY: 'auto',
},
});
No close button
Set showCloseButton={false} and close the dialog through your own footer
buttons instead.
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
export default function DialogNoCloseButton() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Open dialog
</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Confirm your email</DialogTitle>
<DialogDescription>
We sent a confirmation link to your inbox. This dialog only
closes through the buttons below.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>
Resend later
</DialogClose>
<DialogClose render={<Button />}>Got it</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
API reference
Built on Base UI Dialog. The tables below cover the props this library adds or changes — every other prop is forwarded to the underlying Base UI part; see the Base UI Dialog API reference for the full list.
Dialog (Root)
| Prop | Type | Default | Description |
|---|---|---|---|
open |
boolean |
— | Controlled open state. |
defaultOpen |
boolean |
false |
|
onOpenChange |
(open: boolean) => void |
— |
DialogContent
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton |
boolean |
true |
Renders an X close button in the top-right corner. |
style |
StyleXStyles |
— | StyleX styles merged last — always win over the component’s own styles. |
Styling
DialogContent, DialogOverlay, DialogHeader, DialogFooter, DialogTitle, DialogDescription accept style (StyleXStyles, merged last so caller overrides always win) plus all native props of the element they render.
DialogTrigger / DialogClose accept render: <DialogTrigger render={<Button />}>. Always include a DialogTitle for accessibility.
Theming note: dialogs portal to <body> — apply themes (e.g. darkTheme) to <html>, not a wrapper element.