HextaUI

Dialog

Formlar ve odaklı görevler için sayfanın üzerinde bir pencere; sabit üst bilgi ve alt bilgi, iç içe geçme ve telefonlarda kaydırılabilir bir alt sayfa içerir.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

export function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Edit profile
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here. Click save when you’re done.
          </DialogDescription>
        </DialogHeader>
        <DialogBody>
          <FieldGroup>
            <Field>
              <FieldLabel>Name</FieldLabel>
              <Input defaultValue="Olivia Martin" autoComplete="name" />
            </Field>
            <Field>
              <FieldLabel>Username</FieldLabel>
              <Input defaultValue="@olivia" autoComplete="username" />
            </Field>
          </FieldGroup>
        </DialogBody>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>
            Cancel
          </DialogClose>
          <DialogClose render={<Button />}>Save changes</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/dialog.json

Bileşeni, HextaUI tema token'larını ve bileşenin bağımlı olduğu tüm HextaUI bileşenlerini ekler.

import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
<Dialog>
  <DialogTrigger render={<Button variant="outline" />}>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Edit profile</DialogTitle>
      <DialogDescription>Make changes to your profile.</DialogDescription>
    </DialogHeader>
    <DialogBody>{/* fields */}</DialogBody>
    <DialogFooter>
      <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
      <Button>Save changes</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
Dialog
├── DialogTrigger
└── DialogContent
    ├── DialogHeader
    │   ├── DialogTitle
    │   └── DialogDescription
    ├── DialogBody
    └── DialogFooter
        └── DialogClose

Form

Formu <DialogBody /> içinde tutun ve alt bilgideki submit düğmesini form ile ona yöneltin. Enter gönderir ve değer kaydedilince diyalog onOpenChange aracılığıyla kapanır.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

export function DialogForm() {
  const [open, setOpen] = React.useState(false)
  const [name, setName] = React.useState("Acme")
  const [draft, setDraft] = React.useState(name)
  const formId = React.useId()

  return (
    <div className="flex flex-col items-center gap-3">
      <Dialog
        open={open}
        onOpenChange={(nextOpen) => {
          if (nextOpen) {
            setDraft(name)
          }
          setOpen(nextOpen)
        }}
      >
        <DialogTrigger render={<Button variant="outline" />}>
          Rename workspace
        </DialogTrigger>
        <DialogContent size="sm">
          <DialogHeader>
            <DialogTitle>Rename workspace</DialogTitle>
            <DialogDescription>
              Everyone in the workspace will see the new name.
            </DialogDescription>
          </DialogHeader>
          <DialogBody>
            <form
              id={formId}
              onSubmit={(event) => {
                event.preventDefault()
                setName(draft.trim() || name)
                setOpen(false)
              }}
            >
              <FieldGroup>
                <Field>
                  <FieldLabel>Workspace name</FieldLabel>
                  <Input
                    name="name"
                    value={draft}
                    onChange={(event) => setDraft(event.target.value)}
                    required
                  />
                </Field>
              </FieldGroup>
            </form>
          </DialogBody>
          <DialogFooter>
            <DialogClose render={<Button variant="outline" />}>
              Cancel
            </DialogClose>
            <Button type="submit" form={formId}>
              Save
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <p className="text-sm text-muted-foreground">Workspace: {name}</p>
    </div>
  )
}

Özel kapatma düğmesi

Köşe düğmesini içerikte showCloseButton={false} ile gizleyin ve alt bilgiye showCloseButton ile bir Close düğmesi ekleyin.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

export function DialogCustomCloseButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>Share</DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Share link</DialogTitle>
          <DialogDescription>
            Anyone who has this link will be able to view this.
          </DialogDescription>
        </DialogHeader>
        <DialogBody>
          <Field>
            <FieldLabel>Link</FieldLabel>
            <Input
              defaultValue="https://hextaui.com/docs/installation"
              readOnly
            />
          </Field>
        </DialogBody>
        <DialogFooter showCloseButton />
      </DialogContent>
    </Dialog>
  )
}

Kapatma düğmesi yok

Düğme olmadan da diyalog Esc, dışarı tıklama veya telefonlarda aşağı kaydırma ile kapanır.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DialogNoCloseButton() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        No close button
      </DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>No close button</DialogTitle>
          <DialogDescription>
            Close it with Esc, by clicking outside, or by swiping down on a
            phone.
          </DialogDescription>
        </DialogHeader>
      </DialogContent>
    </Dialog>
  )
}

Boyutlar

size daha büyük ekranlardaki azami genişliği belirler: sm, default veya lg.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

const sizes = [
  { size: "sm", label: "Small" },
  { size: "default", label: "Default" },
  { size: "lg", label: "Large" },
] as const

export function DialogSizes() {
  return (
    <div className="flex flex-wrap justify-center gap-2">
      {sizes.map(({ size, label }) => (
        <Dialog key={size}>
          <DialogTrigger render={<Button variant="outline" />}>
            {label}
          </DialogTrigger>
          <DialogContent size={size}>
            <DialogHeader>
              <DialogTitle>{label} dialog</DialogTitle>
              <DialogDescription>
                On phones every size becomes a full-width bottom sheet.
              </DialogDescription>
            </DialogHeader>
            <DialogFooter>
              <DialogClose render={<Button />}>Done</DialogClose>
            </DialogFooter>
          </DialogContent>
        </Dialog>
      ))}
    </div>
  )
}

<DialogBody /> içindeki uzun içerik kayar; üst ve alt bilgi yerinde kalır, böylece eylemler her zaman erişilebilir olur.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DialogStickyFooter() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Terms of service
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Terms of service</DialogTitle>
          <DialogDescription>Last updated October 2026.</DialogDescription>
        </DialogHeader>
        <DialogBody>
          <div className="flex flex-col gap-3 text-sm text-muted-foreground">
            {Array.from({ length: 20 }, (_, index) => (
              <p key={index}>
                {index + 1}. By using the service you agree to keep your account
                secure and to use it in line with these terms and any laws that
                apply to you.
              </p>
            ))}
          </div>
        </DialogBody>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>
            Decline
          </DialogClose>
          <DialogClose render={<Button />}>Accept</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Kaydırılabilir içerik

Alt bilgi olmadan gövde başlığın altında kayar ve alt dolgusunu korur.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DialogScrollableContent() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Release notes
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>What’s new</DialogTitle>
          <DialogDescription>Version 2.4.0</DialogDescription>
        </DialogHeader>
        <DialogBody>
          <div className="flex flex-col gap-3 text-sm text-muted-foreground">
            {Array.from({ length: 24 }, (_, index) => (
              <p key={index}>
                {index + 1}. Improved performance of the dashboard charts and
                fixed an issue where filters reset after navigation.
              </p>
            ))}
          </div>
        </DialogBody>
      </DialogContent>
    </Dialog>
  )
}

Kontrollü

Trigger olmadan koddan açmak için open ve onOpenChange geçin.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog"

export function DialogControlled() {
  const [open, setOpen] = React.useState(false)

  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button variant="outline" onClick={() => setOpen(true)}>
        Open from state
      </Button>
      <span className="text-sm text-muted-foreground">
        {open ? "Open" : "Closed"}
      </span>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Controlled dialog</DialogTitle>
            <DialogDescription>
              The open state lives in your component. Focus returns to the
              button that opened it.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <DialogClose render={<Button />}>Done</DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  )
}

İç içe

Başka bir diyaloğun içinden açılan bir diyalog veya alert dialog üste yığılır. Üsttekinin arkasındaki geri çekilir ve onu daha açık bir arka plan kaplar. Esc yalnızca en üsttekini kapatır.

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DialogNested() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>
        Workspace settings
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Workspace settings</DialogTitle>
          <DialogDescription>
            Manage members and the danger zone.
          </DialogDescription>
        </DialogHeader>
        <DialogBody>
          <div className="flex flex-wrap gap-2">
            <Dialog>
              <DialogTrigger render={<Button variant="outline" />}>
                Invite member
              </DialogTrigger>
              <DialogContent size="sm">
                <DialogHeader>
                  <DialogTitle>Invite member</DialogTitle>
                  <DialogDescription>
                    The settings dialog steps back while this one is open.
                  </DialogDescription>
                </DialogHeader>
                <DialogFooter>
                  <DialogClose render={<Button />}>Send invite</DialogClose>
                </DialogFooter>
              </DialogContent>
            </Dialog>
            <AlertDialog>
              <AlertDialogTrigger render={<Button variant="destructive" />}>
                Delete workspace
              </AlertDialogTrigger>
              <AlertDialogContent>
                <AlertDialogHeader>
                  <AlertDialogTitle>Delete workspace?</AlertDialogTitle>
                  <AlertDialogDescription>
                    All projects in this workspace will be removed.
                  </AlertDialogDescription>
                </AlertDialogHeader>
                <AlertDialogFooter>
                  <AlertDialogCancel>Cancel</AlertDialogCancel>
                  <AlertDialogAction variant="destructive">
                    Delete
                  </AlertDialogAction>
                </AlertDialogFooter>
              </AlertDialogContent>
            </AlertDialog>
          </div>
        </DialogBody>
      </DialogContent>
    </Dialog>
  )
}

Ayrık tetikleyiciler

Tek bir diyaloğu birçok trigger arasında paylaşmak için createDialogHandle() ile bir handle oluşturun. Her trigger, diyaloğun bir render fonksiyonu aracılığıyla okuduğu bir payload geçirir.

"use client"

import { Button } from "@/components/ui/button"
import {
  createDialogHandle,
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

const people = createDialogHandle<{ name: string; role: string }>()

export function DialogDetachedTriggers() {
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <DialogTrigger
        handle={people}
        payload={{ name: "Ada Lovelace", role: "Owner" }}
        render={<Button variant="outline" size="sm" />}
      >
        Ada
      </DialogTrigger>
      <DialogTrigger
        handle={people}
        payload={{ name: "Linus Torvalds", role: "Member" }}
        render={<Button variant="outline" size="sm" />}
      >
        Linus
      </DialogTrigger>
      <Dialog handle={people}>
        {({ payload }) => (
          <DialogContent size="sm">
            <DialogHeader>
              <DialogTitle>{payload?.name}</DialogTitle>
              <DialogDescription>Role: {payload?.role}</DialogDescription>
            </DialogHeader>
            <DialogFooter>
              <DialogClose render={<Button />}>Done</DialogClose>
            </DialogFooter>
          </DialogContent>
        )}
      </Dialog>
    </div>
  )
}

Sağdan sola

İçeriğe de dir geçin; çünkü içerik, RTL kapsayıcınızın dışındaki bir portal içinde render edilir.

import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

export function DialogRtl() {
  return (
    <div dir="rtl">
      <Dialog>
        <DialogTrigger render={<Button variant="outline" />}>
          تعديل الملف الشخصي
        </DialogTrigger>
        <DialogContent dir="rtl">
          <DialogHeader>
            <DialogTitle>تعديل الملف الشخصي</DialogTitle>
            <DialogDescription>
              قم بإجراء تغييرات على ملفك الشخصي هنا. انقر على حفظ عند الانتهاء.
            </DialogDescription>
          </DialogHeader>
          <DialogBody>
            <FieldGroup>
              <Field>
                <FieldLabel>الاسم</FieldLabel>
                <Input defaultValue="ليلى أحمد" />
              </Field>
              <Field>
                <FieldLabel>اسم المستخدم</FieldLabel>
                <Input defaultValue="@layla" dir="ltr" />
              </Field>
            </FieldGroup>
          </DialogBody>
          <DialogFooter>
            <DialogClose render={<Button variant="outline" />}>
              إلغاء
            </DialogClose>
            <DialogClose render={<Button />}>حفظ التغييرات</DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  )
}
TuşAction
EnterSpaceTrigger üzerinde, diyaloğu açar ve odağı ilk kontrolüne taşır.
TabShift+TabOdağı kontroller arasında taşır. Odak içeride kalır.
EscEn üstteki diyaloğu kapatır ve odağı trigger'ına geri verir.
  • İçerikte role="dialog" bulunur; başlığıyla etiketlenir ve açıklamasıyla tanımlanır. Her zaman bir <DialogTitle /> ekleyin.
  • Fare veya klavyeyle odak ilk kontrolde başlar. Dokunmayla diyaloğun kendisinde başlar; böylece ekran klavyesi, kişiler bir alan seçmeden içeriği örtmez. Değiştirmek için initialFocus geçin.
  • Köşedeki kapatma düğmesi “Close” olarak etiketlenir; arkadaki sayfa inert olur ve kaydırılamaz.
  • Telefonlarda, kapatmak için aşağı kaydırılabilen bir alt sheet'e dönüşür. Azaltılmış hareket açıkken ölçeklenmek veya kaymak yerine solar.

<Sheet /> aracılığıyla Base UI drawer üzerine kuruludur. Her parça, sardığı primitive veya öğenin prop'larını kabul eder.

PropTürVarsayılan
open
boolean–
defaultOpen
booleanfalse
onOpenChange
(open: boolean, details) => void–
onOpenChangeCompleteAçma ya da kapatma animasyonundan sonra çağrılır.
(open: boolean) => void–
modal
boolean | "trap-focus"true
disablePointerDismissalDışarıdaki tıklamalarda diyaloğu açık tutar.
booleanfalse
handleAyrık tetikleyicileri bağlar.
DialogHandle<Payload>–
actionsRefDiyaloğu kod üzerinden kapatın veya kaldırın.
RefObject<{ close, unmount }>–
children
ReactNode | ({ payload }) => ReactNode–
PropTürVarsayılan
handle
DialogHandle<Payload>–
payloadDiyaloğun render fonksiyonuna geçirilir.
Payload–
render
ReactElement | (props, state) => ReactElement<button>
ÖznitelikAçıklama
data-slot="dialog-trigger"CSS'te tetikleyicileri hedefleyin.
data-popup-openDiyaloğu açıkken bulunur.
PropTürVarsayılan
size
"sm" | "default" | "lg""default"
showCloseButtonKapatma düğmesini köşede gösterir.
booleantrue
initialFocus
boolean | RefObject | (openType) => HTMLElement | booleanFirst control, or the dialog on touch
finalFocus
boolean | RefObject | (closeType) => HTMLElement | booleanThe trigger
dirDiyalog sağdan sola olacaksa ayarlayın.
"ltr" | "rtl"–
render
ReactElement | (props, state) => ReactElement<div>
ÖznitelikAçıklama
data-slot="dialog-content"CSS'te diyaloğu hedefleyin.
data-sizeGeçerli boyut.
data-openAçıkken bulunur.
data-starting-styleDiyalog açılma animasyonu sırasında bulunur.
data-ending-styleDiyalog kapanma animasyonu sırasında bulunur.
data-nested-drawer-openÜstte iç içe bir diyalog açıkken bulunur.
data-swipingTelefonda kaydırılırken bulunur.
--nested-drawersBunun üstünde kaç diyalog açık.

Başlığı ve açıklamayı üst üste dizen bir <div>. Kapatma düğmesi için yer bırakır.

ÖznitelikAçıklama
data-slot="dialog-header"CSS'te başlığı hedefleyin.

İçerik ekrandan uzunsa kayan, üst ve alt bilgiyi yerinde tutan bir <div>.

ÖznitelikAçıklama
data-slot="dialog-body"CSS'te gövdeyi hedefleyin.

Eylemler için bir <div>. Düğmeler telefonlarda tam genişlikte üst üste dizilir; ilki en altta olur.

PropTürVarsayılan
showCloseButtonAlt öğelerden sonra outline bir Close düğmesi ekler.
booleanfalse
ÖznitelikAçıklama
data-slot="dialog-footer"CSS'te alt bölümü hedefleyin.
PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<h2>
ÖznitelikAçıklama
data-slot="dialog-title"CSS'te başlığı hedefleyin.
PropTürVarsayılan
renderBirden çok paragraf içerdiğinde render={<div />} kullanın.
ReactElement | (props, state) => ReactElement<p>
ÖznitelikAçıklama
data-slot="dialog-description"CSS'te açıklamayı hedefleyin.
PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<button>
ÖznitelikAçıklama
data-slot="dialog-close"CSS'te kapatma düğmelerini hedefleyin.

<DialogContent /> ikisini de zaten oluşturur. Bunları yalnızca özel bir popup oluştururken kullanın.

PropTürVarsayılan
keepMountedPortal üzerinde, diyaloğu kapalıyken de DOM'da tutar.
booleanfalse

Sayfanın herhangi bir yerindeki <DialogTrigger /> öğelerini tek bir <Dialog /> ile bağlayan bir handle döndürür. Payload'u bir generic ile tiplendirin: createDialogHandle<{ name: string }>().

Kullanıldığı bloklar

Dialog üzerine kurulan bloklar.