Lewati ke konten

Dialog peringatan

Sebuah jendela pop-up yang menghentikan alur proses untuk tindakan yang merusak — menghapus, keluar, membatalkan — dengan sentuhan desain neobrutalis berupa garis tepi dan bayangan.

import {
  AlertDialog,
  AlertDialogAction,

Alert dialog menyela pengguna dengan modal yang menuntut pilihan eksplisit — mengeklik di luarnya tidak akan menutupnya. Dibangun di atas komponen Base UI Alert Dialog dan ditata dengan resep neobrutalis: garis tepi tebal, bayangan keras, dan tipografi tebal.

Gunakan saat:

  • Konfirmasi destruktif — menghapus akun, mengeluarkan anggota tim, menghapus seluruh proyek.
  • Operasi yang tidak bisa dibatalkan — menerbitkan, mengirim, menyerahkan; apa pun yang tidak punya undo.
  • Penjaga perubahan belum tersimpan — beri peringatan sebelum pengguna meninggalkan formulir dengan perubahan yang belum disimpan.

Instalasi

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
npx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json

Pasang dependensi berikut:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Salin dan tempel kode berikut ke proyek Anda.

components/ui/alert-dialog.tsx
"use client"

import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"

function AlertDialog({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}

function AlertDialogTrigger({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
  return (
    <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
  )
}

function AlertDialogPortal({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
  return (
    <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
  )
}

function AlertDialogOverlay({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
  return (
    <AlertDialogPrimitive.Overlay
      data-slot="alert-dialog-overlay"
      className={cn(
        "fixed inset-0 z-50 bg-foreground/20 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 AlertDialogContent({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
  size?: "default" | "sm"
}) {
  return (
    <AlertDialogPortal>
      <AlertDialogOverlay />
      <AlertDialogPrimitive.Content
        data-slot="alert-dialog-content"
        data-size={size}
        className={cn(
          "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded border-2 bg-popover p-4 text-popover-foreground shadow-md duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm 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}
      />
    </AlertDialogPortal>
  )
}

function AlertDialogHeader({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-header"
      className={cn(
        "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogFooter({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-footer"
      className={cn(
        "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t-2 bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogMedia({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-media"
      className={cn(
        "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogTitle({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
  return (
    <AlertDialogPrimitive.Title
      data-slot="alert-dialog-title"
      className={cn(
        "font-head text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogDescription({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
  return (
    <AlertDialogPrimitive.Description
      data-slot="alert-dialog-description"
      className={cn(
        "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogAction({
  className,
  variant = "default",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Action
        data-slot="alert-dialog-action"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

function AlertDialogCancel({
  className,
  variant = "outline",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Cancel
        data-slot="alert-dialog-cancel"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

export {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogTitle,
  AlertDialogTrigger,
}

Perbarui jalur import agar sesuai dengan struktur proyek Anda.

Penggunaan

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
<AlertDialog>
  <AlertDialogTrigger render={<Button variant="outline" />}>
    Show Dialog
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone. This will permanently delete your account
        from our servers.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Continue</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Komposisi

Gunakan komposisi berikut untuk membangun AlertDialog:

AlertDialog
├── AlertDialogTrigger
└── AlertDialogContent
    ├── AlertDialogHeader
    │   ├── AlertDialogMedia
    │   ├── AlertDialogTitle
    │   └── AlertDialogDescription
    └── AlertDialogFooter
        ├── AlertDialogCancel
        └── AlertDialogAction

Contoh

Dasar

Dialog peringatan dasar dengan judul, deskripsi, serta tombol batal dan lanjut.

import {
  AlertDialog,
  AlertDialogAction,

Kecil

Gunakan prop size="sm" untuk memperkecil dialog peringatan.

import {
  AlertDialog,
  AlertDialogAction,

Media

Gunakan komponen AlertDialogMedia untuk menambahkan elemen media, seperti ikon atau gambar, ke dialog peringatan.

import { CircleFadingPlusIcon } from "lucide-react"

import {

Kecil dengan media

Gunakan prop size="sm" untuk memperkecil dialog peringatan dan komponen AlertDialogMedia untuk menambahkan elemen media, seperti ikon atau gambar.

import { BluetoothIcon } from "lucide-react"

import {

Destruktif

Gunakan komponen AlertDialogAction untuk menambahkan tombol aksi destruktif ke dialog peringatan.

import { Trash2Icon } from "lucide-react"

import {

RTL

Untuk mengaktifkan dukungan RTL di Neobrutalism, lihat panduan konfigurasi RTL.

"use client"

import { BluetoothIcon } from "lucide-react"

Aksesibilitas

Alert dialog mengikuti pola Alert Dialog WAI-ARIA: popup-nya dirender dengan role="alertdialog" yang terhubung ke judul dan deskripsi melalui aria-labelledby dan aria-describedby, dan fokus terkunci di dalamnya selama dialog terbuka.

Interaksi keyboard:

TombolAksi
Space / EnterMembuka dialog dari pemicu yang difokus
Tab / Shift + TabMemutar fokus di antara elemen dialog yang dapat difokus
EscMenutup dialog dan mengembalikan fokus ke pemicu

Referensi API

size

Gunakan prop size pada komponen AlertDialogContent untuk mengatur ukuran dialog peringatan. Prop ini menerima nilai berikut:

PropTypeDefault
size"default" | "sm""default"

Untuk informasi lebih lanjut tentang komponen lain dan prop-nya, lihat dokumentasi Base UI.