Dialog
---import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose } from "@/components/starwind/dialog";import { Button } from "@/components/starwind/button";---
<Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div class="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <Button variant="outline">Cancel</Button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent></Dialog>import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose } from "@/components/starwind/dialog";import { Button } from "@/components/starwind/button";
export function Example() { return ( <Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div className="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <Button variant="outline">Cancel</Button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent> </Dialog> );}<script setup lang="ts">import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose,} from "@/components/starwind/dialog";import { Button, ButtonVariants } from "@/components/starwind/button";</script>
<template> <Dialog> <DialogTrigger asChild> <button :class="ButtonVariants.button({})" type="button">Open Dialog</button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div class="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Cancel </button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent> </Dialog></template>Installation
pnpx starwind@latest add dialog --framework astronpx starwind@latest add dialog --framework astroyarn dlx starwind@latest add dialog --framework astropnpx starwind@latest add dialog --framework reactnpx starwind@latest add dialog --framework reactyarn dlx starwind@latest add dialog --framework reactpnpx starwind@latest add dialog --framework vuenpx starwind@latest add dialog --framework vueyarn dlx starwind@latest add dialog --framework vueUsage
---import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose } from "@/components/starwind/dialog";import { Button } from "@/components/starwind/button";---
<Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div class="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <Button variant="outline">Cancel</Button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent></Dialog>import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose } from "@/components/starwind/dialog";import { Button } from "@/components/starwind/button";
export function Example() { return ( <Dialog> <DialogTrigger asChild> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div className="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <Button variant="outline">Cancel</Button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent> </Dialog> );}<script setup lang="ts">import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose,} from "@/components/starwind/dialog";import { Button, ButtonVariants } from "@/components/starwind/button";</script>
<template> <Dialog> <DialogTrigger asChild> <button :class="ButtonVariants.button({})" type="button">Open Dialog</button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Example Dialog</DialogTitle> <DialogDescription> This is a simple dialog example that demonstrates the basic functionality. </DialogDescription> </DialogHeader> <div class="py-4">Your dialog content goes here.</div> <DialogFooter> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Cancel </button> </DialogClose> <Button>Save Changes</Button> </DialogFooter> </DialogContent> </Dialog></template>Composition
Use this structure for a dialog:
Dialogโโโ DialogTriggerโโโ DialogContent โโโ DialogHeader โ โโโ DialogTitle โ โโโ DialogDescription โโโ DialogFooterDialogContent includes a close button. Use DialogClose when another control also needs to close the dialog.
When to Use
Use Dialog for focused content that appears above the page, such as a form, settings panel, or details that require a user response.
With Form
Dialogs are commonly used with forms for data input.
Tip
Open up the dev tools console to see the form data on submission.
---import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DialogClose,} from "@/components/starwind/dialog";
import { Input } from "@/components/starwind/input";
import { Label } from "@/components/starwind/label";
import { Button } from "@/components/starwind/button";---
<Dialog> <DialogTrigger asChild> <Button variant="outline">Edit Profile</Button> </DialogTrigger> <DialogContent class="sm:max-w-[450px]"> <form id="edit-profile-form" method="dialog" class="flex flex-col gap-4"> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Make changes to your profile here. Click save when you're done. </DialogDescription> </DialogHeader> <div class="grid gap-4 py-4"> <div class="grid grid-cols-4 items-center gap-4"> <Label for="name-edit" class="text-right"> Name </Label> <Input id="name-edit" name="name" placeholder="Pedro Duarte" class="col-span-3" /> </div> <div class="grid grid-cols-4 items-center gap-4"> <Label for="username" class="text-right"> Username </Label> <Input id="username" name="username" placeholder="@peduarte" class="col-span-3" /> </div> </div> <DialogFooter> <DialogClose asChild> <Button type="button" variant="outline">Cancel</Button> </DialogClose> <Button type="submit">Save changes</Button> </DialogFooter> </form> </DialogContent></Dialog>
<script> function handleFormSubmit() { const form = document.querySelector("#edit-profile-form") as HTMLFormElement;
if (form) { form.addEventListener("submit", (e) => { e.preventDefault(); const formData = new FormData(form); const formValues = Object.fromEntries(formData.entries());
// demo form data logging console.log("Form submission values:", formValues);
// You can add additional logic here like: // - Form validation // - API submission // - Success/error handling }); } }
handleFormSubmit();
document.addEventListener("astro:after-swap", handleFormSubmit);</script>import type { FormEvent } from "react";import { Button } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";
export function Example() { function handleSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); console.log( "Form submission values:", Object.fromEntries(new FormData(event.currentTarget).entries()), ); }
return ( <Dialog> <DialogTrigger asChild><Button variant="outline">Edit Profile</Button></DialogTrigger> <DialogContent className="sm:max-w-[450px]"> <form className="flex flex-col gap-4" onSubmit={handleSubmit}> <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Make changes to your profile here. Click save when you're done. </DialogDescription> </DialogHeader> <div className="grid gap-4 py-4"> <div className="grid grid-cols-4 items-center gap-4"> <Label htmlFor="name-edit" className="text-right">Name</Label> <Input id="name-edit" name="name" placeholder="Pedro Duarte" className="col-span-3" /> </div> <div className="grid grid-cols-4 items-center gap-4"> <Label htmlFor="username" className="text-right">Username</Label> <Input id="username" name="username" placeholder="@peduarte" className="col-span-3" /> </div> </div> <DialogFooter> <DialogClose asChild><Button type="button" variant="outline">Cancel</Button></DialogClose> <Button type="submit">Save changes</Button> </DialogFooter> </form> </DialogContent> </Dialog> );}<script setup lang="ts">import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, DialogClose,} from "@/components/starwind/dialog";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Button, ButtonVariants } from "@/components/starwind/button";
function submit(event: Event) { const form = event.currentTarget as HTMLFormElement; console.log("Form submission values:", Object.fromEntries(new FormData(form)));}</script>
<template> <Dialog> <DialogTrigger asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Edit Profile </button> </DialogTrigger> <DialogContent class="sm:max-w-[450px]"> <form @submit.prevent="submit" id="edit-profile-form" method="dialog" class="flex flex-col gap-4" > <DialogHeader> <DialogTitle>Edit profile</DialogTitle> <DialogDescription> Make changes to your profile here. Click save when you're done. </DialogDescription> </DialogHeader> <div class="grid gap-4 py-4"> <div class="grid grid-cols-4 items-center gap-4"> <Label for="name-edit" class="text-right"> Name </Label> <Input id="name-edit" name="name" placeholder="Pedro Duarte" class="col-span-3" /> </div> <div class="grid grid-cols-4 items-center gap-4"> <Label for="username" class="text-right"> Username </Label> <Input id="username" name="username" placeholder="@peduarte" class="col-span-3" /> </div> </div> <DialogFooter> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Cancel </button> </DialogClose> <Button type="submit">Save changes</Button> </DialogFooter> </form> </DialogContent> </Dialog></template>Multiple Triggers
You can have multiple triggers open the same dialog, including external triggers outside the Dialog wrapper. Add an id to <Dialog /> and pass that ID through targetId on each external <DialogTrigger />.
This button is outside the Dialog component but opens the same dialog:
---import { Button } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Textarea } from "@/components/starwind/textarea";---
<Dialog id="contact-us-dialog"> <DialogTrigger asChild> <Button variant="outline">Contact Us</Button> </DialogTrigger> <DialogContent class="sm:max-w-[450px]"> <form id="contact-form" method="dialog" class="flex flex-col gap-4"> <DialogHeader> <DialogTitle>Contact Us</DialogTitle> <DialogDescription> Send us a message and we'll get back to you shortly. </DialogDescription> </DialogHeader> <div class="grid gap-6 py-4"> <div class="grid gap-2"> <Label for="email">Email</Label> <Input id="email" name="email" type="email" placeholder="your@email.com" /> </div> <div class="grid gap-2"> <Label for="subject">Subject</Label> <Input id="subject" name="subject" placeholder="How can we help?" /> </div> <div class="grid gap-2"> <Label for="message">Message</Label> <Textarea id="message" name="message" placeholder="Tell us what you need..." class="min-h-[80px]" /> </div> </div> <DialogFooter> <DialogClose asChild> <Button type="button" variant="outline">Cancel</Button> </DialogClose> <Button type="submit">Send Message</Button> </DialogFooter> </form> </DialogContent></Dialog>
<!-- External trigger that opens the same dialog --><div class="mt-8"> <p class="text-muted-foreground mb-2 text-sm"> This button is outside the Dialog component but opens the same dialog: </p> <DialogTrigger targetId="contact-us-dialog" class="mt-2" asChild> <Button variant="primary">Contact Us (External)</Button> </DialogTrigger></div>
<script> const setupForm = () => { const form = document.getElementById("contact-form") as HTMLFormElement; if (!form) return;
form.addEventListener("submit", (e) => { e.preventDefault(); const formData = new FormData(form); const formValues = Object.fromEntries(formData.entries());
// demo form data logging console.log("Form submission values:", formValues);
// You can add additional logic here like: // - Form validation // - API submission // - Success/error handling }); };
setupForm();
document.addEventListener("astro:after-swap", setupForm);</script>import type { FormEvent } from "react";import { Button } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Textarea } from "@/components/starwind/textarea";
export function Example() { function handleSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); console.log( "Form submission values:", Object.fromEntries(new FormData(event.currentTarget).entries()), ); }
return ( <> <Dialog id="contact-us-dialog"> <DialogTrigger asChild><Button variant="outline">Contact Us</Button></DialogTrigger> <DialogContent className="sm:max-w-[450px]"> <form className="flex flex-col gap-4" onSubmit={handleSubmit}> <DialogHeader> <DialogTitle>Contact Us</DialogTitle> <DialogDescription>Send us a message and we'll get back to you shortly.</DialogDescription> </DialogHeader> <div className="grid gap-6 py-4"> <div className="grid gap-2"> <Label htmlFor="email">Email</Label> <Input id="email" name="email" type="email" placeholder="your@email.com" /> </div> <div className="grid gap-2"> <Label htmlFor="subject">Subject</Label> <Input id="subject" name="subject" placeholder="How can we help?" /> </div> <div className="grid gap-2"> <Label htmlFor="message">Message</Label> <Textarea id="message" name="message" placeholder="Tell us what you need..." className="min-h-[80px]" /> </div> </div> <DialogFooter> <DialogClose asChild><Button type="button" variant="outline">Cancel</Button></DialogClose> <Button type="submit">Send Message</Button> </DialogFooter> </form> </DialogContent> </Dialog> <div className="mt-8"> <p className="text-muted-foreground mb-2 text-sm"> This button is outside the Dialog component but opens the same dialog: </p> <DialogTrigger targetId="contact-us-dialog" className="mt-2" asChild> <Button variant="primary">Contact Us (External)</Button> </DialogTrigger> </div> </> );}<script setup lang="ts">import { Button, ButtonVariants } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Textarea } from "@/components/starwind/textarea";import { ref } from "vue";import type { DialogOpenChangeDetails } from "@starwind-ui/vue/dialog";
function submit(event: Event) { const form = event.currentTarget as HTMLFormElement; console.log("Form submission values:", Object.fromEntries(new FormData(form)));}
const open = ref(false);function onOpenChange(nextOpen: boolean, detail: DialogOpenChangeDetails) { console.log("Dialog open state:", nextOpen, detail);}</script>
<template> <Dialog v-model:open="open" @open-change="onOpenChange" id="contact-us-dialog"> <DialogTrigger asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Contact Us </button> </DialogTrigger> <DialogContent class="sm:max-w-[450px]"> <form @submit.prevent="submit" id="contact-form" method="dialog" class="flex flex-col gap-4"> <DialogHeader> <DialogTitle>Contact Us</DialogTitle> <DialogDescription> Send us a message and we'll get back to you shortly. </DialogDescription> </DialogHeader> <div class="grid gap-6 py-4"> <div class="grid gap-2"> <Label for="email">Email</Label> <Input id="email" name="email" type="email" placeholder="your@email.com" /> </div> <div class="grid gap-2"> <Label for="subject">Subject</Label> <Input id="subject" name="subject" placeholder="How can we help?" /> </div> <div class="grid gap-2"> <Label for="message">Message</Label> <Textarea id="message" name="message" placeholder="Tell us what you need..." class="min-h-[80px]" /> </div> </div> <DialogFooter> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Cancel </button> </DialogClose> <Button type="submit">Send Message</Button> </DialogFooter> </form> </DialogContent> </Dialog>
<!-- External trigger that opens the same dialog --> <div class="mt-8"> <p class="text-muted-foreground mb-2 text-sm"> This button is outside the Dialog component but opens the same dialog: </p> <Button variant="primary" class="mt-2" @click="open = true">Contact Us (External)</Button> </div></template>Nested Dialogs
New You can nest dialogs within other dialogs to create multi-level workflows. The nested dialog will appear on top of the parent dialog, and only the topmost dialog will close when pressing Escape or clicking outside.
---import { Button } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";---
<Dialog> <DialogTrigger asChild> <Button variant="outline">Open Parent Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Parent Dialog</DialogTitle> <DialogDescription> This is the parent dialog. You can open a nested dialog from here. </DialogDescription> </DialogHeader> <DialogFooter class="mt-4"> <Dialog> <DialogTrigger asChild> <Button class="w-full sm:w-auto">Open Nested</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Nested Dialog</DialogTitle> <DialogDescription> This is the nested dialog. The parent will not close when clicking outside.</DialogDescription > </DialogHeader> <DialogFooter class="mt-4"> <DialogClose asChild> <Button variant="outline">Close Nested</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> <DialogClose asChild> <Button variant="outline" class="w-full sm:w-auto">Close Parent</Button> </DialogClose> </DialogFooter> </DialogContent></Dialog>import { Button } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";
export function Example() { return ( <> <Dialog> <DialogTrigger asChild> <Button variant="outline">Open Parent Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Parent Dialog</DialogTitle> <DialogDescription> This is the parent dialog. You can open a nested dialog from here. </DialogDescription> </DialogHeader> <DialogFooter className="mt-4"> <Dialog> <DialogTrigger asChild> <Button className="w-full sm:w-auto">Open Nested</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Nested Dialog</DialogTitle> <DialogDescription> This is the nested dialog. The parent will not close when clicking outside.</DialogDescription > </DialogHeader> <DialogFooter className="mt-4"> <DialogClose asChild> <Button variant="outline">Close Nested</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> <DialogClose asChild> <Button variant="outline" className="w-full sm:w-auto">Close Parent</Button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> </> );}<script setup lang="ts">import { Button, ButtonVariants } from "@/components/starwind/button";import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger,} from "@/components/starwind/dialog";</script>
<template> <Dialog> <DialogTrigger asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Open Parent Dialog </button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Parent Dialog</DialogTitle> <DialogDescription> This is the parent dialog. You can open a nested dialog from here. </DialogDescription> </DialogHeader> <DialogFooter class="mt-4"> <Dialog> <DialogTrigger asChild> <button :class="ButtonVariants.button({ class: 'w-full sm:w-auto' })" type="button"> Open Nested </button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Nested Dialog</DialogTitle> <DialogDescription> This is the nested dialog. The parent will not close when clicking outside.</DialogDescription > </DialogHeader> <DialogFooter class="mt-4"> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline' })" type="button"> Close Nested </button> </DialogClose> </DialogFooter> </DialogContent> </Dialog> <DialogClose asChild> <button :class="ButtonVariants.button({ variant: 'outline', class: 'w-full sm:w-auto' })" type="button" > Close Parent </button> </DialogClose> </DialogFooter> </DialogContent> </Dialog></template>API Reference
Styled Component API
These props are added or materially changed by the installed styled component. Standard HTML attributes remain available through the inherited interfaces noted below. Expand a prop to see named type definitions and framework-specific imports. Follow the Primitive and Runtime links for lower-level behavior props.
Dialog Trigger
Inherits button attributes.
Contains the following additional props:
asChild boolean false
- Description
- Merges the component behavior and props into its child element.
- Classification
- Wrapper prop
Dialog Close
Inherits button attributes.
Contains the following additional props:
asChild boolean false
- Description
- Merges the component behavior and props into its child element.
- Classification
- Wrapper prop
Primitive And Runtime API
Use these references when you need the lower-level behavior APIs behind Dialog.
Primitive API
Runtime API
- Dialog primitive
createDialogfrom@starwind-ui/runtime/dialog
Changelog
View version history v2.0.2 2 releases
v2.0.2
- Named the generated aggregate default export so React and Astro tooling can identify the installed component cleanly.
v2.0.0
- Rebuilt Dialog on Starwind Runtime for modal state, focus management, dismissal, and nesting.
- See the Dialog Primitive for the underlying unstyled anatomy and behavior API.