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

Loading...
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&apos;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:

npx mcoli-ui@latest add mc-dialog
pnpm dlx mcoli-ui@latest add mc-dialog
yarn dlx mcoli-ui@latest add mc-dialog
bunx mcoli-ui@latest add mc-dialog

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install @base-ui/react lucide-react
pnpm add @base-ui/react lucide-react
yarn add @base-ui/react lucide-react
bun add @base-ui/react lucide-react
Copy the source code
'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,};
Copy button dependency or replace render usage

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.

Adjust import paths

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>
<DialogFooter showCloseButton={false}>
  <DialogClose render={<McButton variant="secondary">Cancel</McButton>} />
  <DialogClose render={<McButton variant="primary">Save changes</McButton>} />
</DialogFooter>

API Reference

Dialog

PropTypeDefaultDescription
...propsDialogPrimitive.Root.Props-All root props from @base-ui/react/dialog

DialogTrigger

PropTypeDefaultDescription
...propsDialogPrimitive.Trigger.Props-Trigger element and interactions

DialogContent

PropTypeDefaultDescription
showCloseButtonbooleantrueShows or hides the top-right close button
classNamestringundefinedAdditional CSS classes
...propsDialogPrimitive.Popup.Props-All popup props from Base UI

DialogHeader

PropTypeDefaultDescription
classNamestringundefinedAdditional CSS classes
...propsReact.ComponentProps<'div'>-Native div props

DialogFooter

PropTypeDefaultDescription
showCloseButtonbooleanfalseAppends a default secondary close button
classNamestringundefinedAdditional CSS classes
...propsReact.ComponentProps<'div'>-Native div props

DialogTitle

PropTypeDefaultDescription
classNamestringundefinedAdditional CSS classes
...propsDialogPrimitive.Title.Props-All title props from Base UI

DialogDescription

PropTypeDefaultDescription
classNamestringundefinedAdditional CSS classes
...propsDialogPrimitive.Description.Props-All description props from Base UI

DialogClose, DialogOverlay, DialogPortal

All these helpers forward their corresponding @base-ui/react/dialog primitive props directly.

On this page