Dialog
A dialog is a focused, overlay-based container used to present critical information or request user input that requires immediate attention. It appears above the application content, blocks underlying interaction, and guides the user toward a required decision or acknowledgement.
import {Dialog} from "@qualcomm-ui/react/dialog"Examples
Sizes
The dialog component has two sizes: sm and md. Size governs the width of the dialog, as well as the content's spacing, font size, and padding.
import type {ReactElement} from "react"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog, useQdsDialogContext} from "@qualcomm-ui/react/dialog"
export function DialogSizesDemo(): ReactElement {
return (
<div className="flex flex-col gap-4">
<Dialog.Root size="sm">
<Dialog.Trigger>
<Button emphasis="primary" variant="fill">
Open Dialog (sm)
</Button>
</Dialog.Trigger>
<DialogContent />
</Dialog.Root>
<Dialog.Root size="md">
<Dialog.Trigger>
<Button emphasis="primary" variant="fill">
Open Dialog (md)
</Button>
</Dialog.Trigger>
<DialogContent />
</Dialog.Root>
</div>
)
}
function DialogContent() {
const context = useQdsDialogContext()
return (
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.Heading>Dialog Title ({context.size})</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>
<LoremIpsum />
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button size="sm" variant="outline">
Cancel
</Button>
</Dialog.CloseTrigger>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
)
}Indicator Emphasis
Use the emphasis prop to change the intent of the dialog's indicator icon.
import type {ReactElement} from "react"
import type {QdsDialogEmphasis} from "@qualcomm-ui/qds-core/dialog"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
const emphasis: QdsDialogEmphasis[] = [
"neutral",
"info",
"success",
"warning",
"danger",
]
export function DialogEmphasisDemo(): ReactElement {
return (
<div className="flex flex-col gap-4">
{emphasis.map((emp) => (
<Dialog.Root key={emp} emphasis={emp}>
<Dialog.Trigger>
<Button size="sm" variant="outline">
{emp}
</Button>
</Dialog.Trigger>
<DialogContent />
</Dialog.Root>
))}
</div>
)
}
function DialogContent() {
return (
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.CloseButton />
<Dialog.IndicatorIcon />
<Dialog.Heading>Dialog Title</Dialog.Heading>
<Dialog.Description>
<LoremIpsum />
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
)
}Placement
Use the placement prop to change the placement of the dialog.
import type {ReactElement} from "react"
import type {QdsDialogPlacement} from "@qualcomm-ui/qds-core/dialog"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
const placement: QdsDialogPlacement[] = ["top", "center", "bottom"]
export function DialogPlacementDemo(): ReactElement {
return (
<div className="flex flex-col gap-4">
{placement.map((plc) => (
<Dialog.Root key={plc} placement={plc}>
<Dialog.Trigger>
<Button emphasis="neutral" size="sm" variant="outline">
Open Dialog ({plc})
</Button>
</Dialog.Trigger>
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.Heading>Dialog Title</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>Dialog Description</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
</Dialog.Root>
))}
</div>
)
}Controlled State
Dialog visibility can be controlled using the open, onOpenChange and defaultOpen props. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
export function DialogControlledStateDemo(): ReactElement {
const [open, setOpen] = useState<boolean>(false)
return (
<Dialog.Root onOpenChange={setOpen} open={open}>
<Dialog.Trigger>
<Button emphasis="primary" variant="fill">
Open Dialog
</Button>
</Dialog.Trigger>
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.Heading>Dialog Title</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>Dialog Description</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
</Dialog.Root>
)
}Outside Scroll
The default scrollBehavior is outside, which makes the entire page scrollable when modal content exceeds viewport height. The modal and backdrop move with the page scroll instead of creating an internal scroll area within the modal.
import type {ReactElement} from "react"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
export function DialogOutsideScrollDemo(): ReactElement {
return (
<Dialog.Root>
<Dialog.Trigger>
<Button emphasis="primary" variant="fill">
Open Dialog
</Button>
</Dialog.Trigger>
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.Heading>Dialog Title</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>
<LoremIpsum numParagraphs={20} />
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
</Dialog.Root>
)
}Inside Scroll
Set the scrollBehavior prop to inside to create an internal scroll area within the modal.
import type {ReactElement} from "react"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
export function DialogInsideScrollDemo(): ReactElement {
return (
<Dialog.Root scrollBehavior="inside">
<Dialog.Trigger>
<Button emphasis="primary" variant="fill">
Open Dialog
</Button>
</Dialog.Trigger>
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.Heading>Dialog Title</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>
<LoremIpsum numParagraphs={20} />
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Dialog.CloseTrigger>
</Dialog.Footer>
</Dialog.FloatingPortal>
</Dialog.Root>
)
}Alert Dialog
Set the role prop to alertdialog to change the dialog component to an alert dialog. This is recommended for situations where the dialog is used to notify users of urgent information that demands their immediate attention.
import type {ReactElement} from "react"
import {Trash2} from "lucide-react"
import {Button} from "@qualcomm-ui/react/button"
import {Dialog} from "@qualcomm-ui/react/dialog"
export function DialogAlertDialogDemo(): ReactElement {
return (
<Dialog.Root emphasis="danger" role="alertdialog">
<Dialog.Trigger>
<Button emphasis="danger" endIcon={Trash2} variant="outline">
Delete Account
</Button>
</Dialog.Trigger>
<Dialog.FloatingPortal>
<Dialog.Body>
<Dialog.IndicatorIcon />
<Dialog.Heading>Are you sure?</Dialog.Heading>
<Dialog.CloseButton />
<Dialog.Description>
This action cannot be undone. This will permanently delete your
account and remove your data from our systems.
</Dialog.Description>
</Dialog.Body>
<Dialog.Footer>
<Dialog.CloseTrigger>
<Button emphasis="neutral" size="sm" variant="outline">
Cancel
</Button>
</Dialog.CloseTrigger>
<Button emphasis="danger" size="sm" variant="fill">
Confirm
</Button>
</Dialog.Footer>
</Dialog.FloatingPortal>
</Dialog.Root>
)
}Shortcuts
<Dialog.FloatingPortal>
A helper component that combines the portal, positioner, and content components. This shortcut is equivalent to:
<Portal {...props}>
<Dialog.Backdrop {...backdropProps} />
<Dialog.Positioner {...positionerProps}>
<Dialog.Content {...contentProps}>{children}</Dialog.Content>
</Dialog.Positioner>
</Portal>API
<Dialog.Root>
stringbooleanbooleanboolean'ltr' | 'rtl'
| 'info'
| 'success'
| 'warning'
| 'danger'
| 'neutral'
() => HTMLElement
() =>
| Node
| ShadowRoot
| Document
boolean() => HTMLElement
booleanboolean(
event: KeyboardEvent,
) => void
VoidFunction(
event: FocusOutsideEvent,
) => void
(
event: InteractOutsideEvent,
) => void
(
open: boolean,
) => void
- open:The next value of the open state.
(
event: PointerDownOutsideEvent,
) => void
(
event: CustomEvent<{
originalIndex: number
originalLayer: HTMLElement
targetIndex: number
targetLayer: HTMLElement
}>,
) => void
booleanArray<
() => Element
>
- should not have pointer-events disabled
- should not trigger the dismiss event
| 'top'
| 'center'
| 'bottom'
booleanbooleanboolean| 'dialog'
| 'alertdialog'
| 'inside'
| 'outside'
'sm' | 'md'
booleanbooleanboolean<Dialog.Backdrop>
<div> element by default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__backdrop'data-dialog-part'backdrop'data-state| 'open'
| 'closed'
hiddenboolean<Dialog.Positioner>
<div> element by
default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__positioner'data-dialog-part'positioner'data-placement| 'top'
| 'center'
| 'bottom'
data-scroll-behavior| 'inside'
| 'outside'
data-size'sm' | 'md'
<Dialog.Content>
<section> element by default.<Dialog.Root>
<Dialog.Positioner>
<Dialog.Content></Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__content'data-dialog-part'content'data-scroll-behavior| 'inside'
| 'outside'
data-size'sm' | 'md'
data-state| 'open'
| 'closed'
hiddenbooleantabIndex-1
<Dialog.Body>
<div> element by
default.| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__body'data-dialog-part'body'data-size'sm' | 'md'
<Dialog.Heading>
<h2> element by default.<Dialog.Content>
<Dialog.Body>
<Dialog.Heading>Title...</Dialog.Heading>
// ... other
</Dialog.Body>
</Dialog.Content>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__heading'data-dialog-part'heading'data-size'sm' | 'md'
<Dialog.Footer>
<div> element by default.| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__footer'data-dialog-part'footer'data-size'sm' | 'md'
<Dialog.IndicatorIcon>
<span> element by default.| LucideIcon
| ReactNode
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__indicator-icon'data-emphasis| 'info'
| 'success'
| 'warning'
| 'danger'
| 'neutral'
data-size'sm' | 'md'
<Dialog.CloseButton>
<button> element by default.LucideIconstring| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__close-button'data-dialog-part'close-trigger'