Components
- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Dialog
- Empty
- Field
- Fieldset
- Form
- Frame
- Group
- Input
- Label
- Menu
- Meter
- Number Field
- Pagination
- Popover
- Preview Card
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Skeleton
- Slider
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Toolbar
- Tooltip
Dialog
A popup that opens on top of the entire page.
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
import { Form } from "@/components/ui/form"
export default function DialogDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Open Dialog
</DialogTrigger>
<DialogPopup className="sm:max-w-sm">
<Form>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're
done.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<Field>
<FieldLabel>Name</FieldLabel>
<FieldControl type="text" defaultValue="Connor Love" />
</Field>
<Field>
<FieldLabel>Username</FieldLabel>
<FieldControl type="text" defaultValue="@loveconnor" />
</Field>
</div>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>
Cancel
</DialogClose>
<Button type="submit">Save</Button>
</DialogFooter>
</Form>
</DialogPopup>
</Dialog>
)
}
Installation
npx love-ui@latest add dialogUsage
import {
Dialog,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"<Dialog>
<DialogTrigger>Open Dialog</DialogTrigger>
<DialogPopup>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>Dialog Description</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose>Close</DialogClose>
</DialogFooter>
</DialogPopup>
</Dialog>Examples
Open from a Menu
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
} from "@/components/ui/dialog"
import {
Menu,
MenuItem,
MenuPopup,
MenuTrigger,
} from "@/components/ui/menu"
export default function DialogFromMenuDemo() {
const [dialogOpen, setDialogOpen] = React.useState(false)
return (
<>
<Menu>
<MenuTrigger render={<Button variant="outline" />}>
Open menu
</MenuTrigger>
<MenuPopup align="start">
<MenuItem onClick={() => setDialogOpen(true)}>Open dialog</MenuItem>
</MenuPopup>
</Menu>
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogPopup>
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
<DialogDescription>Change your preferences</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Close</DialogClose>
</DialogFooter>
</DialogPopup>
</Dialog>
</>
)
}
Nested Dialogs
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Field, FieldControl, FieldLabel } from "@/components/ui/field"
export default function DialogNestedDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Open parent
</DialogTrigger>
<DialogPopup showCloseButton={false}>
<DialogHeader>
<DialogTitle>Manage team member</DialogTitle>
<DialogDescription>
View and manage a user in your team.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4">
<div className="grid gap-1">
<p className="text-sm text-muted-foreground">Name</p>
<p className="text-sm font-medium">Connor Love</p>
</div>
<div className="grid gap-1">
<p className="text-sm text-muted-foreground">Email</p>
<p className="text-sm font-medium">clove@loveui.dev</p>
</div>
</div>
<DialogFooter>
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Edit details
</DialogTrigger>
<DialogPopup showCloseButton={false}>
<DialogHeader>
<DialogTitle>Edit details</DialogTitle>
<DialogDescription>
Make changes to the member's information.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<Field>
<FieldLabel>Name</FieldLabel>
<FieldControl type="text" defaultValue="Connor Love" />
</Field>
<Field>
<FieldLabel>Email</FieldLabel>
<FieldControl type="text" defaultValue="clove@loveui.dev" />
</Field>
</div>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>
Cancel
</DialogClose>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogPopup>
</Dialog>
</DialogFooter>
</DialogPopup>
</Dialog>
)
}
Close Confirmation
"use client"
import * as React from "react"
import {
AlertDialog,
AlertDialogClose,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogPopup,
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogDescription,
DialogFooter,
DialogHeader,
DialogPopup,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Field } from "@/components/ui/field"
import { Form } from "@/components/ui/form"
import { Textarea } from "@/components/ui/textarea"
export default function DialogCloseConfirmationDemo() {
const [dialogOpen, setDialogOpen] = React.useState(false)
const [confirmOpen, setConfirmOpen] = React.useState(false)
const [value, setValue] = React.useState("")
return (
<Dialog
open={dialogOpen}
onOpenChange={(o) => {
if (!o && value) {
setConfirmOpen(true)
} else {
setDialogOpen(o)
}
}}
>
<DialogTrigger render={<Button variant="outline" />}>
Compose
</DialogTrigger>
<DialogPopup showCloseButton={false}>
<DialogHeader>
<DialogTitle>New message</DialogTitle>
<DialogDescription>Type something and try closing.</DialogDescription>
</DialogHeader>
<Form
onSubmit={(event) => {
event.preventDefault()
// Close the dialog when submitting
setDialogOpen(false)
}}
>
<Field>
<Textarea
value={value}
onChange={(e) => setValue(e.target.value)}
/>
</Field>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>
Cancel
</DialogClose>
<Button
onClick={() => {
setValue("")
setDialogOpen(false)
}}
>
Send
</Button>
</DialogFooter>
</Form>
</DialogPopup>
{/* Confirmation dialog */}
<AlertDialog open={confirmOpen} onOpenChange={setConfirmOpen}>
<AlertDialogPopup>
<AlertDialogHeader>
<AlertDialogTitle>Discard changes?</AlertDialogTitle>
<AlertDialogDescription>
Your message will be lost.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogClose render={<Button variant="ghost" />}>
Go back
</AlertDialogClose>
<Button
onClick={() => {
setConfirmOpen(false)
setValue("")
setDialogOpen(false)
}}
>
Discard
</Button>
</AlertDialogFooter>
</AlertDialogPopup>
</AlertDialog>
</Dialog>
)
}
Comparing with Radix / shadcn
If you’re already familiar with Radix UI and shadcn/ui, this guide highlights the small differences and similarities so you can get started with loveui quickly.
Quick Checklist
- Replace
asChild→renderonDialogTriggerand closing buttons - Prefer
DialogPopup;DialogContentremains for legacy - If you used
asChildon any other parts, switch to therenderprop
Comparison Example
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Show Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>Dialog Description</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="ghost">Cancel</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog><Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Show Dialog
</DialogTrigger>
<DialogPopup>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>Dialog Description</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
</DialogFooter>
</DialogPopup>
</Dialog>