Dialog
Show a modal with configurable layout and accessible actions
- Comes with styling, yet completely customizable and themeable.
- Accepts animations, themes, size props and more.
- Accessible with dev-time checks to ensure ARIA props. Dialog is a great way to show content inside a new floating window above content. Dialogs automatically stack above other overlays. Be sure to open the code example above for a copy-paste implementation.
Installation
Dialog is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/dialogFor native apps, we recommend setting up native portals to preserve React context inside Dialog content.
Anatomy
import { Dialog } from '@hanzo/gui' // or '@hanzogui/dialog'
export default () => (
<Dialog>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
{/* ... */}
</Dialog.Content>
</Dialog.Portal>
{/* Optional: Control focus behavior */}
<Dialog.FocusScope loop trapped focusOnIdle={true}>
<Dialog.FocusScope.Scope>
{/* Focus scope will be applied to children */}
</Dialog.FocusScope.Scope>
</Dialog.FocusScope>
</Dialog>
)Scoping
Dialog supports scoping which lets you mount one or more Dialog instances at the root of your app, while having a deeply nested child Trigger or Content attach to the proper parent Dialog instance.
In performance sensitive areas you may want to take advantage of this, it allows you to only need to render the Dialog.Trigger inside the sensitive area as Dialogs aren't the cheapest component - they have a lot of functionality.
Here's the basic anatomy of using scope and placing your Dialog higher up for
performance:
import { Dialog } from '@hanzo/gui'
// in your root layout:
export default ({ children }) => (
<Dialog scope="user-profile">
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
{/* ... */}
</Dialog.Content>
</Dialog.Portal>
{/* the rest of your app, note that it's inside of Dialog */}
{children}
</Dialog>
)export default () => (
<Dialog.Trigger scope="user-profile">
<Button>Open Profile</Button>
</Dialog.Trigger>
)Note that the Trigger scope ties to the Dialog scope.
Dismissal Behavior
By default, dialogs can be dismissed by:
- Clicking outside the dialog content (on the overlay)
- Pressing the Escape key
- Clicking a Dialog.Close element
Modal vs Non-Modal Dialogs
- Modal dialogs (
modal={true}, which is the default):- In v1, have
disableOutsidePointerEventsset totrueby default - Still dismiss on outside click, but prevent interaction with elements behind the dialog
- Prevent right-click dismissal (right-clicks on the overlay are ignored)
- In v1, have
- Non-modal dialogs (
modal={false}):- Allow interaction with elements behind the dialog
- Dismiss on any outside click
- Do not trap focus
Preventing Outside Dismissal
To prevent a dialog from closing when clicking outside:
<Dialog.Content
onPointerDownOutside={(event) => {
event.preventDefault()
}}
>
{/* Dialog contents */}
</Dialog.Content>API Reference
Dialog
Contains every component for the dialog. Beyond Hanzo GUI Props, adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| children | React.ReactNode | - | ✓ |
| size | SizeTokens | - | - |
| open | boolean | - | - |
| defaultOpen | boolean | - | - |
| onOpenChange | (open: boolean) => void | - | - |
| modal | boolean | true | - |
| keepChildrenMounted | boolean | false | - |
| disableRemoveScroll | boolean | - | - |
Dialog.Trigger
Just Hanzo GUI Props.
Dialog.Portal
Renders Dialog into appropriate container. Beyond Hanzo GUI Props, adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| forceMount | boolean | - | - |
| unstyled | boolean | - | - |
Dialog.Content
Main container for Dialog content, this is where you should apply animations.
Beyond Hanzo GUI Props, adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| forceMount | boolean | - | - |
| unstyled | boolean | - | - |
| disableOutsidePointerEvents | boolean | - | - |
Dialog.Overlay
Displays behind Content. Beyond Hanzo GUI Props, adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| forceMount | boolean | - | - |
Dialog.Title
Required. Can wrap in VisuallyHidden to hide.
Defaults to H2, see Headings.
Dialog.Description
Required. Can wrap in VisuallyHidden to hide.
Defaults to Paragraph, see Paragraph.
Dialog.Close
Closes the Dialog, accepts the same props as YStack. Recommended to use with
your own component and asChild.
| Prop | Type | Default | Required |
|---|---|---|---|
| displayWhenAdapted | boolean | - | - |
Just Hanzo GUI Props.
Dialog.FocusScope
Provides access to the underlying FocusScope component used by Dialog for focus management. Can be used to control focus behavior from a parent component.
| Prop | Type | Default | Required |
|---|---|---|---|
| enabled | boolean | true | - |
| loop | boolean | false | - |
| trapped | boolean | false | - |
| focusOnIdle | boolean | number | false | - |
| onMountAutoFocus | (event: Event) => void | - | - |
| onUnmountAutoFocus | (event: Event) => void | - | - |
Dialog.Sheet
When used with Adapt, Dialog will render as a sheet when that breakpoint is
active.
See Sheet for more props.
Must use Adapt.Contents inside the Dialog.Sheet.Frame to insert the contents
given to Dialog.Content
import { Dialog } from '@hanzo/gui' // or '@hanzogui/dialog'
export default () => (
<Dialog>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
{/* ... */}
</Dialog.Content>
</Dialog.Portal>
{/* optionally change to sheet when small screen */}
<Dialog.Adapt when="max-md">
<Dialog.Sheet>
<Dialog.Sheet.Frame>
<Dialog.Adapt.Contents />
</Dialog.Sheet.Frame>
<Dialog.Sheet.Overlay />
</Dialog.Sheet>
</Dialog.Adapt>
</Dialog>
)Note that Dialog.Sheet currently doesn't preserve state of the contents when it transitions between Sheet and Portal. In the future, we can do this on the web using react-reparenting.
Last updated on