MC Dialog
A modal dialog for important content or interactions.
Overview
The mc-dialog component is a modal surface built on top of @base-ui/react/dialog with
MicroClub UI styling.
It provides a composable API with dedicated slots for trigger, content, header, description, footer, and close actions, while keeping accessibility and keyboard interactions handled by Base UI.
Preview
import { McButton } from '../ui/mc-button';import { McDialog, McDialogClose, McDialogContent, McDialogDescription, McDialogFooter, McDialogHeader, McDialogTitle, McDialogTrigger,} from '@/registry/ui/mc-dialog';export default function McDialogDemo() { return ( <div className="py-6"> <McDialog> <McDialogTrigger render={ <McButton variant="primary" size="md"> Open dialog </McButton> } /> <McDialogContent className="gap-6"> <McDialogHeader className="gap-2.5"> <McDialogTitle>Edit profile</McDialogTitle> <McDialogDescription> Make changes to your profile here. Click save when you're done. </McDialogDescription> </McDialogHeader> <form className="grid gap-5" onSubmit={(event) => event.preventDefault()}> <div className="grid gap-2.5"> <label htmlFor="name" className="font-sans text-sm leading-5 font-medium tracking-normal text-muted-foreground" > Name </label> <input id="name" defaultValue="Pedro Duarte" className="h-9 w-[375px] max-w-full rounded-md border border-blue-primary-200 bg-transparent px-3 py-1 font-sans text-sm leading-5 font-normal tracking-normal text-foreground opacity-100 outline-none focus-visible:ring-3 focus-visible:ring-ring/50" /> </div> <div className="grid gap-2.5"> <label htmlFor="username" className="font-sans text-sm leading-5 font-medium tracking-normal text-muted-foreground" > Username </label> <input id="username" defaultValue="@peduarte" className="h-9 w-[375px] max-w-full rounded-md border border-blue-primary-200 bg-transparent px-3 py-1 font-sans text-sm leading-5 font-normal tracking-normal text-foreground opacity-100 outline-none focus-visible:ring-3 focus-visible:ring-ring/50" /> </div> </form> <McDialogFooter showCloseButton={false}> <McDialogClose render={ <McButton variant="secondary" size="md"> Cancel </McButton> } /> <McDialogClose render={ <McButton variant="primary" size="md"> Save changes </McButton> } /> </McDialogFooter> </McDialogContent> </McDialog> </div> );}Add to Your Project
Deploy the mc-dialog directly to your components/ui directory:
Manual Setup
If you prefer manual control:
'use client';import * as React from 'react';import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';import { XIcon } from 'lucide-react';import { cn } from '@/lib/utils';import { McButton } from '@/registry/ui/mc-button';function McDialog({ ...props }: DialogPrimitive.Root.Props) { return <DialogPrimitive.Root data-slot="dialog" {...props} />;}function McDialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;}function McDialogPortal({ ...props }: DialogPrimitive.Portal.Props) { return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;}function McDialogClose({ ...props }: DialogPrimitive.Close.Props) { return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;}function McDialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) { return ( <DialogPrimitive.Backdrop data-slot="dialog-overlay" className={cn( 'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0', className )} {...props} /> );}function McDialogContent({ className, children, showCloseButton = true, ...props}: DialogPrimitive.Popup.Props & { showCloseButton?: boolean;}) { return ( <McDialogPortal> <McDialogOverlay /> <DialogPrimitive.Popup data-slot="dialog-content" className={cn( 'fixed top-1/2 left-1/2 z-50 grid w-[min(423px,calc(100%-2rem))] gap-4 rounded-lg border bg-popover p-6 text-sm text-popover-foreground opacity-100 -translate-x-1/2 -translate-y-1/2 duration-100 outline-none sm:min-h-[344px] data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95', className )} {...props} > {children} {showCloseButton && ( <DialogPrimitive.Close data-slot="dialog-close" render={ <McButton variant="tertiary" className="absolute top-2 right-2 !bg-transparent text-muted-foreground shadow-none hover:!bg-transparent hover:text-foreground active:text-foreground focus:outline-none focus-visible:ring-0 focus-visible:ring-transparent focus-visible:shadow-none" size="sm" icon="only" iconDefinition={<XIcon className="size-4" />} > <span className="sr-only">Close</span> </McButton> } /> )} </DialogPrimitive.Popup> </McDialogPortal> );}function McDialogHeader({ className, ...props }: React.ComponentProps<'div'>) { return ( <div data-slot="dialog-header" className={cn('flex flex-col gap-2', className)} {...props} /> );}function McDialogFooter({ className, showCloseButton = false, children, ...props}: React.ComponentProps<'div'> & { showCloseButton?: boolean;}) { return ( <div data-slot="dialog-footer" className={cn( 'flex flex-col-reverse gap-2 !border-t-0 pt-1 pb-4 sm:flex-row sm:items-center sm:justify-end', className )} {...props} > {children} {showCloseButton && ( <DialogPrimitive.Close render={<McButton variant="secondary">Close</McButton>} /> )} </div> );}function McDialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { return ( <DialogPrimitive.Title data-slot="dialog-title" className={cn('font-sans text-lg leading-7 font-semibold text-card-foreground', className)} {...props} /> );}function McDialogDescription({ className, ...props }: DialogPrimitive.Description.Props) { return ( <DialogPrimitive.Description data-slot="dialog-description" className={cn( 'font-sans text-sm leading-5 font-normal tracking-normal text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground', className )} {...props} /> );}export { McDialog, McDialogClose, McDialogContent, McDialogDescription, McDialogFooter, McDialogHeader, McDialogOverlay, McDialogPortal, McDialogTitle, McDialogTrigger,};The default close controls inside DialogContent and DialogFooter use McButton.
If you don't use mc-button, replace those render props with your own button component.
Ensure your @/lib/utils and @/components/ui/* aliases are correctly configured.
Usage
import { McButton } from '@/components/ui/mc-button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/mc-dialog';
export default function Example() {
return (
<Dialog>
<DialogTrigger
render={
<McButton variant="primary" size="md">
Open dialog
</McButton>
}
/>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<DialogFooter showCloseButton={false}>
<DialogClose
render={
<McButton variant="secondary" size="md">
Cancel
</McButton>
}
/>
<DialogClose
render={
<McButton variant="primary" size="md">
Save changes
</McButton>
}
/>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Examples
Basic Dialog
<Dialog>
<DialogTrigger render={<McButton>Open dialog</McButton>} />
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>Hide Top Close Button
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Custom Header</DialogTitle>
</DialogHeader>
</DialogContent>Footer With Action Buttons
<DialogFooter showCloseButton={false}>
<DialogClose render={<McButton variant="secondary">Cancel</McButton>} />
<DialogClose render={<McButton variant="primary">Save changes</McButton>} />
</DialogFooter>API Reference
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
...props | DialogPrimitive.Root.Props | - | All root props from @base-ui/react/dialog |
DialogTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
...props | DialogPrimitive.Trigger.Props | - | Trigger element and interactions |
DialogContent
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | true | Shows or hides the top-right close button |
className | string | undefined | Additional CSS classes |
...props | DialogPrimitive.Popup.Props | - | All popup props from Base UI |
DialogHeader
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefined | Additional CSS classes |
...props | React.ComponentProps<'div'> | - | Native div props |
DialogFooter
| Prop | Type | Default | Description |
|---|---|---|---|
showCloseButton | boolean | false | Appends a default secondary close button |
className | string | undefined | Additional CSS classes |
...props | React.ComponentProps<'div'> | - | Native div props |
DialogTitle
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefined | Additional CSS classes |
...props | DialogPrimitive.Title.Props | - | All title props from Base UI |
DialogDescription
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefined | Additional CSS classes |
...props | DialogPrimitive.Description.Props | - | All description props from Base UI |
DialogClose, DialogOverlay, DialogPortal
All these helpers forward their corresponding @base-ui/react/dialog primitive props directly.