Dialog
A focused surface for decisions that need attention.
import { Dialog } from "@sagui/ui";Loading demo
When to use
- Confirmations and decisions that must interrupt, such as Delete project.
- Short forms like rename or invite that fit in one focused panel.
- Flows where the dialog title changes between steps and should crossfade in place.
When not to use
- Use drawer for long forms or detail panels that keep the page in context.
- Use bottom-sheet for mobile-first secondary tasks with snap heights.
- Use popover for light, non-modal content anchored to a trigger.
Installation
Install the package and import the styles once. The installation guide covers the Tailwind setup.
$ npm install @sagui/uiUsage
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@sagui/ui";
import { Button } from "@sagui/ui";
export function RenameProject() {
return (
<Dialog>
<DialogTrigger asChild><Button>Rename</Button></DialogTrigger>
<DialogContent title="Rename project" description="This changes the URL too.">
<input defaultValue="Arc" aria-label="Project name" />
<DialogClose asChild><Button>Save</Button></DialogClose>
</DialogContent>
</Dialog>
);
}Examples
Controlled
Pass open and onOpenChange to open it from anywhere in your code.
Loading demo
Copy that changes while open
The title and description reword in place, so a multi-step flow never swaps the whole surface.
Loading demo
API reference
Dialog
Root that tracks open state so the content can animate out. Controlled or uncontrolled.
| Prop | Type | Default | Description |
|---|---|---|---|
open |
boolean |
– | Controlled open state. |
defaultOpen |
boolean |
false |
Initial open state when uncontrolled. |
onOpenChange |
(open: boolean) => void |
– | Called when the dialog opens or closes. |
...props |
ComponentPropsWithoutRef<typeof DialogPrimitive.Root> |
– | Other Radix Dialog root props, such as modal. |
DialogTrigger
Radix Dialog.Trigger. Use asChild to wrap your own button.
| Prop | Type | Default | Description |
|---|---|---|---|
...props |
ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger> |
– | Radix trigger props, including asChild. |
DialogContent
Portaled overlay and panel with a titled header and built-in close button.
| Prop | Type | Default | Description |
|---|---|---|---|
title (required) |
string |
– | Dialog title, rendered as Radix Dialog.Title. Changes crossfade while open. |
description |
string |
– | Optional supporting line, rendered as Dialog.Description. |
children (required) |
ReactNode |
– | Body content, usually a form or actions. |
...props |
ComponentPropsWithoutRef<typeof DialogPrimitive.Content> |
– | Radix content props such as className, onEscapeKeyDown, and onPointerDownOutside. |
DialogClose
Radix Dialog.Close for custom cancel or confirm buttons.
| Prop | Type | Default | Description |
|---|---|---|---|
...props |
ComponentPropsWithoutRef<typeof DialogPrimitive.Close> |
– | Radix close props, including asChild. |
Keyboard interactions
| Keys | Action |
|---|---|
| Escape | Closes the dialog and returns focus to the trigger. |
| TaborShift+Tab | Cycles focus within the dialog. |
Accessibility
- Radix renders role="dialog" with aria-modal, traps focus, and restores it to the trigger on close.
- title and description are wired to aria-labelledby and aria-describedby.
- The close button carries aria-label="Close dialog".
Motion
- The overlay fades while the panel rises 8px and scales from 0.96 on a smooth spring; closing is shorter and retargets from the current state.
- Title and description changes rise in with a soft blur.
- Reduced motion uses a plain opacity fade.
Responsive behavior
- The panel is min(100vw minus a 32px gutter, 440px) wide and centered, so it fits phones without extra CSS.
- Height caps at the viewport minus a gutter and the panel scrolls beyond that.
Performance
- The overlay uses a 7px backdrop blur, which can cost frames on low-end devices over busy pages.
- Content renders in a portal only while open and unmounts after the exit animation.
Notes
- Use for decisions that must interrupt: confirmations, short forms. Use drawer for side panels, bottom-sheet for mobile-first secondary tasks, popover for light non-modal content.
- Always compose Dialog > DialogTrigger + DialogContent. Wrap your own buttons with asChild.