Alert Dialog
A simplified wrapper for Shadcn Alert Dialog with flexible option handling and automatic type conversion
Installation
npx shadcn@latest add @glrk-ui/alert-dialogIf you haven't set up the prerequisites yet, check out Prerequest section.
Copy and paste the following code into shadcn alert-dialog component.
type AlertDialogFooterWrapperProps = {
cancel?: React.ReactNode
action?: React.ReactNode
footerCls?: string
actionCls?: string
cancelCls?: string
onAction?: () => void
onCancel?: () => void
}
function AlertDialogFooterWrapper({
cancel,
action,
footerCls,
actionCls,
cancelCls,
onAction = () => { },
onCancel = () => { },
}: AlertDialogFooterWrapperProps) {
return (
<AlertDialogFooter className={footerCls}>
{
cancel &&
<AlertDialogCancel
onClick={onCancel}
className={cancelCls}
asChild={typeof cancel !== "string"}
>
{cancel}
</AlertDialogCancel>
}
{
action &&
<AlertDialogAction
onClick={onAction}
className={actionCls}
asChild={typeof action !== "string"}
>
{action}
</AlertDialogAction>
}
</AlertDialogFooter>
)
}
type AlertDialogWrapperProps = {
title?: React.ReactNode
trigger?: React.ReactNode
children?: React.ReactNode
description?: React.ReactNode
descriptionCls?: string
contentCls?: string
headerCls?: string
titleCls?: string
} & AlertDialogFooterWrapperProps
function AlertDialogWrapper({
trigger,
title = "Are you absolutely sure?",
description = "This action cannot be undone. This will permanently remove your data from our servers.",
children,
contentCls,
headerCls,
titleCls,
descriptionCls,
cancel = "Cancel",
action = "Confirm",
footerCls,
actionCls,
cancelCls,
onAction,
onCancel,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root> & AlertDialogWrapperProps) {
return (
<AlertDialog {...props}>
{trigger &&
<AlertDialogTrigger asChild={typeof trigger !== "string"}>{trigger}</AlertDialogTrigger>
}
<AlertDialogContent className={contentCls}>
<AlertDialogHeader className={headerCls}>
<AlertDialogTitle className={titleCls}>{title}</AlertDialogTitle>
{description && (
<AlertDialogDescription className={descriptionCls}>
{description}
</AlertDialogDescription>
)}
</AlertDialogHeader>
{children}
{
(!!cancel || !!action) &&
<AlertDialogFooterWrapper
cancel={cancel}
action={action}
footerCls={footerCls}
actionCls={actionCls}
cancelCls={cancelCls}
onAction={onAction}
onCancel={onCancel}
/>
}
</AlertDialogContent>
</AlertDialog>
)
}Add AlertDialogWrapper and AlertDialogFooterWrapper at the export block.
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
AlertDialogWrapper,
AlertDialogFooterWrapper,
}Installation
npm install @radix-ui/react-alert-dialogalert-dialog.tsx
Copy and paste the following code into your project.
"use client"
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } 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(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
)}
{...props}
/>
)
}
function AlertDialogContent({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
data-slot="alert-dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
)}
{...props}
/>
</AlertDialogPortal>
)
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn("text-lg font-semibold", className)}
{...props}
/>
)
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
function AlertDialogAction({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
return (
<AlertDialogPrimitive.Action
className={cn(buttonVariants(), className)}
{...props}
/>
)
}
function AlertDialogCancel({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
return (
<AlertDialogPrimitive.Cancel
className={cn(buttonVariants({ variant: "outline" }), className)}
{...props}
/>
)
}
type AlertDialogFooterWrapperProps = {
cancel?: React.ReactNode
action?: React.ReactNode
footerCls?: string
actionCls?: string
cancelCls?: string
onAction?: () => void
onCancel?: () => void
}
function AlertDialogFooterWrapper({
cancel,
action,
footerCls,
actionCls,
cancelCls,
onAction = () => { },
onCancel = () => { },
}: AlertDialogFooterWrapperProps) {
return (
<AlertDialogFooter className={footerCls}>
{
cancel &&
<AlertDialogCancel
onClick={onCancel}
className={cancelCls}
asChild={typeof cancel !== "string"}
>
{cancel}
</AlertDialogCancel>
}
{
action &&
<AlertDialogAction
onClick={onAction}
className={actionCls}
asChild={typeof action !== "string"}
>
{action}
</AlertDialogAction>
}
</AlertDialogFooter>
)
}
type AlertDialogWrapperProps = {
title?: React.ReactNode
trigger?: React.ReactNode
children?: React.ReactNode
description?: React.ReactNode
descriptionCls?: string
contentCls?: string
headerCls?: string
titleCls?: string
} & AlertDialogFooterWrapperProps
function AlertDialogWrapper({
trigger,
title = "Are you absolutely sure?",
description = "This action cannot be undone. This will permanently remove your data from our servers.",
children,
contentCls,
headerCls,
titleCls,
descriptionCls,
cancel = "Cancel",
action = "Confirm",
footerCls,
actionCls,
cancelCls,
onAction,
onCancel,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root> & AlertDialogWrapperProps) {
return (
<AlertDialog {...props}>
{trigger &&
<AlertDialogTrigger asChild={typeof trigger !== "string"}>{trigger}</AlertDialogTrigger>
}
<AlertDialogContent className={contentCls}>
<AlertDialogHeader className={headerCls}>
<AlertDialogTitle className={titleCls}>{title}</AlertDialogTitle>
{description && (
<AlertDialogDescription className={descriptionCls}>
{description}
</AlertDialogDescription>
)}
</AlertDialogHeader>
{children}
{
(!!cancel || !!action) &&
<AlertDialogFooterWrapper
cancel={cancel}
action={action}
footerCls={footerCls}
actionCls={actionCls}
cancelCls={cancelCls}
onAction={onAction}
onCancel={onCancel}
/>
}
</AlertDialogContent>
</AlertDialog>
)
}
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
AlertDialogWrapper,
AlertDialogFooterWrapper,
}Done
You can now use AlertDialogWrapper
Usage
Basic
import { AlertDialogWrapper } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export function Basic() {
return (
<AlertDialogWrapper
trigger={<Button>Delete</Button>}
/>
)
}Controlled
import { useState } from "react"
import { AlertDialogWrapper } from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
export function Controlled() {
const [open, setOpen] = useState(false)
return (
<AlertDialogWrapper
open={open}
onOpenChange={setOpen}
trigger={<Button>Controlled</Button>}
onAction={() => setOpen(false)}
/>
)
}
// Programaticaly open model without trigger
export function Controlled2() {
const [open, setOpen] = useState(false)
const updateOpen = () => setOpen(p => !p)
return (
<>
<Button onClick={updateOpen}>Controlled 2</Button>
<AlertDialogWrapper
open={open}
onOpenChange={updateOpen}
onAction={updateOpen}
/>
</>
)
}Custom
export function Custom() {
return (
<AlertDialogWrapper
trigger={<Button>Delete</Button>}
title="Some Custom Modal"
description=""
// to remove footer make cancel and action empty
cancel=""
action=""
>
<div>...</div>
</AlertDialogWrapper>
)
}Reference
AlertDialogFooterWrapper
Prop
Type
AlertDialogWrapper
Prop
Type
Properties of AlertDialogFooterWrapper can be passed directly.