Dialog

A modal window layered over the page for focused tasks or confirmations. Built on Base UI's dialog primitive.

Last updated August 8, 2026

CreditsModifiedPublished

Copied from shadcn/ui.

  • DialogTrigger measures the ambient direction where it renders and passes it to the portaled DialogViewport, so Persian content and logical spacing render correctly even when the dialog is opened from an RTL-only section of an otherwise-LTR page
  • Centers via a CSS grid viewport (grid-rows-[1fr_auto_3fr] with justify-items-center) instead of the usual top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 trick, which sidesteps that trick's RTL bug entirely rather than needing a rtl: correction
  • Adds a DialogPanel that scrolls independently inside a fixed-height popup, so a tall form doesn't push the header/footer off-screen

Overview

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogClose,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogPanel,
  DialogPopup,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
 
export function DialogDemoExample() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Edit profile</Button>} />
      <DialogPopup>
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here. Click save when you&apos;re done.
          </DialogDescription>
        </DialogHeader>
        <DialogPanel className="flex flex-col gap-4">
          <div className="flex flex-col gap-2">
            <Label htmlFor="dialog-demo-name">Name</Label>
            <Input id="dialog-demo-name" defaultValue="Sadegh Alavi" />
          </div>
          <div className="flex flex-col gap-2">
            <Label htmlFor="dialog-demo-username">Username</Label>
            <Input id="dialog-demo-username" defaultValue="@sadegh" />
          </div>
        </DialogPanel>
        <DialogFooter>
          <Button type="submit">Save changes</Button>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
        </DialogFooter>
      </DialogPopup>
    </Dialog>
  )
}

DialogFooter is just another child — skip it for purely informational dialogs that don't need a confirm/cancel pair.

import { BadgeCheckIcon } from "lucide-react"
 
import {
  Dialog,
  DialogDescription,
  DialogHeader,
  DialogPanel,
  DialogPopup,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
 
export function DialogNoFooterExample() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">View receipt</Button>} />
      <DialogPopup>
        <DialogHeader>
          <DialogTitle>Payment successful</DialogTitle>
          <DialogDescription>
            Your subscription is active. A receipt was sent to your email.
          </DialogDescription>
        </DialogHeader>
        <DialogPanel className="flex items-center gap-3">
          <BadgeCheckIcon className="size-8 shrink-0 text-primary" />
          <p className="text-sm text-muted-foreground">
            Nothing left to do here — close this with the button in the corner,
            Escape, or by clicking outside.
          </p>
        </DialogPanel>
      </DialogPopup>
    </Dialog>
  )
}

Installation

$ npx shadcn@latest add https://ui.persian-labs.ir/r/dialog.json

Usage

import {
  Dialog,
  DialogClose,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogPanel,
  DialogPopup,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
 
export function Example() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline">Open</Button>} />
      <DialogPopup>
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here.
          </DialogDescription>
        </DialogHeader>
        <DialogPanel>{/* form fields */}</DialogPanel>
        <DialogFooter>
          <Button type="submit">Save changes</Button>
          <DialogClose render={<Button variant="outline">Cancel</Button>} />
        </DialogFooter>
      </DialogPopup>
    </Dialog>
  )
}

RTL

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogClose,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogPanel,
  DialogPopup,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
 
export function DialogRtlExample() {
  return (
    <Dialog>
      <DialogTrigger
        render={<Button variant="outline">ویرایش پروفایل</Button>}
      />
      <DialogPopup>
        <DialogHeader>
          <DialogTitle>ویرایش پروفایل</DialogTitle>
          <DialogDescription>
            تغییرات را اعمال کنید و روی ذخیره کلیک کنید.
          </DialogDescription>
        </DialogHeader>
        <DialogPanel className="flex flex-col gap-4">
          <div className="flex flex-col gap-2">
            <Label htmlFor="dialog-rtl-name">نام</Label>
            <Input id="dialog-rtl-name" defaultValue="صادق علوی" />
          </div>
          <div className="flex flex-col gap-2">
            <Label htmlFor="dialog-rtl-username">نام کاربری</Label>
            <Input id="dialog-rtl-username" defaultValue="@sadegh" />
          </div>
        </DialogPanel>
        <DialogFooter>
          <Button type="submit">ذخیره تغییرات</Button>
          <DialogClose render={<Button variant="outline">انصراف</Button>} />
        </DialogFooter>
      </DialogPopup>
    </Dialog>
  )
}

API Reference

Dialog

DialogPopup