Skip to Content
ComponentsDialog

Dialog

Display a modal dialog box.

Documentation

Usage

import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; export function App() { return ( <Dialog> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Controlled

Use the open and defaultOpen props to toggle between controlled and uncontrolled usage. And combine it with onOpenChange to listen for changes to the state.

import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; import { useState } from "react"; export function App() { const [open, setOpen] = useState(false); return ( <Dialog onOpenChange={setOpen} open={open}> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Sizes

Use the size prop to change the size of the dialog box.

import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; import { type ComponentPropsWithRef } from "react"; export function App({ size, }: Pick<ComponentPropsWithRef<typeof DialogContent>, "size">) { return ( <Dialog> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent size={size}> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Description

You can add accessible description to the modal using the description prop on DialogHeader component.

import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; export function App() { return ( <Dialog> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent> <DialogHeader description="This is additional description of the modal"> Modal Title </DialogHeader> <DialogFooter> <DialogClose>Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Customize trigger

By default we use the Button component for the dialog trigger which accepts all of the existing button props.

We can also completely change the trigger by using asChild and passing our own component.

import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; import { IconRefresh } from "@tabler/icons-react"; export function App() { return ( <Dialog> <DialogTrigger appearance="primary" aria-label="Re-publish changes" icon={<IconRefresh />} /> <DialogContent> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Form dialogs

Wrap DialogBody and DialogFooter with the DialogForm component to use forms in dialogs.

import { Button, Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogForm, DialogHeader, DialogTrigger, Field, Input, Textarea, } from "@optiaxiom/react"; import { useState } from "react"; export function App() { const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); return ( <Dialog onOpenChange={loading ? undefined : setOpen} open={open}> <DialogTrigger>Create new issue</DialogTrigger> <DialogContent size="sm"> <DialogHeader>Create new issue</DialogHeader> <DialogForm onSubmit={(event) => { event.preventDefault(); setLoading(true); // use form data to perform a server action setTimeout(() => { setLoading(false); setOpen(false); }, 3000); }} > <DialogBody> <Field label="Title"> <Input autoFocus name="title" required /> </Field> <Field label="Description"> <Textarea name="description" required /> </Field> </DialogBody> <DialogFooter> <DialogClose disabled={loading}>Cancel</DialogClose> <Button appearance="primary" disabled={loading} loading={loading} type="submit" > Save </Button> </DialogFooter> </DialogForm> </DialogContent> </Dialog> ); }

Nested dialogs

Combine dialogs to show nested dialogs.

import { Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTrigger, } from "@optiaxiom/react"; export function App() { return ( <Dialog> <DialogTrigger>Open Dialog</DialogTrigger> <DialogContent size="sm"> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <Dialog> <DialogTrigger mr="auto">Open nested dialog</DialogTrigger> <DialogContent size="sm"> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <Dialog> <DialogTrigger mr="auto">Open nested dialog</DialogTrigger> <DialogContent size="sm"> <DialogHeader>Modal Title</DialogHeader> <DialogBody>This is the modal body</DialogBody> <DialogFooter> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> <DialogClose appearance="primary">Close</DialogClose> </DialogFooter> </DialogContent> </Dialog> ); }

Nested alert dialogs

Combine Dialog with AlertDialog to show nested confirmation dialogs.

Use the onOpenChange handler to trigger the nested callback instead of an explicit trigger.

import { AlertDialog, AlertDialogAction, AlertDialogBody, AlertDialogCancel, AlertDialogContent, AlertDialogFooter, AlertDialogHeader, Button, Dialog, DialogBody, DialogClose, DialogContent, DialogFooter, DialogForm, DialogHeader, DialogTrigger, Textarea, } from "@optiaxiom/react"; import { useRef, useState } from "react"; export function App() { const [open, setOpen] = useState(false); const [alertOpen, setAlertOpen] = useState(false); const textRef = useRef<HTMLTextAreaElement>(null); return ( <Dialog onOpenChange={(open) => { if (!open && textRef.current?.value) { setAlertOpen(true); } else { setOpen(open); } }} open={open} > <DialogTrigger>Create new issue</DialogTrigger> <DialogContent size="sm"> <DialogHeader>Create new issue</DialogHeader> <DialogForm onSubmit={(event) => { event.preventDefault(); setOpen(false); }} > <DialogBody> <Textarea autoFocus ref={textRef} required /> </DialogBody> <DialogFooter> <DialogClose>Cancel</DialogClose> <Button appearance="primary">Save</Button> </DialogFooter> </DialogForm> </DialogContent> <AlertDialog onOpenChange={setAlertOpen} open={alertOpen}> <AlertDialogContent onCloseAutoFocus={() => textRef.current?.focus()}> <AlertDialogHeader>Discard issue?</AlertDialogHeader> <AlertDialogBody>All unsaved changes will be lost.</AlertDialogBody> <AlertDialogFooter> <AlertDialogCancel>No, keep editing</AlertDialogCancel> <AlertDialogAction onClick={() => setOpen(false)}> Yes, discard </AlertDialogAction> </AlertDialogFooter> </AlertDialogContent> </AlertDialog> </Dialog> ); }

Related

AlertDialog

Display a modal with important content that expects confirmation from the user.

Props

Dialog

Doesn't render its own HTML element.

Prop

defaultOpen

The initial open state in uncontrolled mode.

false | true

onOpenChange

Handler that is called when the open state changes.

(open: boolean) => void

open

The open state in controlled mode.

false | true

DialogTrigger

Supports all Button props in addition to its own. Renders a <button> element.

Prop

addonAfter

Display content inside the button after children.

ReactNode

addonBefore

Display content inside the button before children.

ReactNode

appearance

Control the appearance by selecting between the different button types.

"default" | "danger" | "primary" | "subtle" | "danger-outline" | "inverse"

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

disabled

Whether the button is disabled.

false | true

icon

Display an icon before or after the button content or omit children to only show the icon.

ReactNode

iconPosition

Control whether to show the icon before or after the button content.

"end" | "start"

loading

Whether to show loading spinner inside the button.

false | true

size

Control the size of the button.

"sm" | "md" | "lg"

square

Whether button should have square shape.

false | true

DialogContent

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

onCloseAutoFocus

Event handler called when auto-focusing on close. Can be prevented.

(event: Event) => void

onEscapeKeyDown

Event handler called when the escape key is down. Can be prevented.

(event: KeyboardEvent) => void

onFocusOutside

Event handler called when the focus moves outside of the DismissableLayer. Can be prevented.

(event: FocusOutsideEvent) => void

onInteractOutside

Event handler called when an interaction happens outside the DismissableLayer. Specifically, when a pointerdown event happens outside or focus moves outside of it. Can be prevented.

(event: FocusOutsideEvent | PointerDownOutsideEvent) => void

onOpenAutoFocus

Event handler called when auto-focusing on open. Can be prevented.

(event: Event) => void

onPointerDownOutside

Event handler called when the a pointerdown event happens outside of the DismissableLayer. Can be prevented.

(event: PointerDownOutsideEvent) => void

size

"sm" | "md" | "lg" | "fullscreen"

Default: md

transitionType

"pop" | "fade" | "slide" | "slidePop"

Default: fade

DialogHeader

Supports all Box props in addition to its own. Renders a <div> element.

Prop

addonAfter

ReactNode

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

description

ReactNode

DialogForm

Supports all Box props in addition to its own. Renders a <form> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DialogBody

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DialogFooter

Supports all Box props in addition to its own. Renders a <div> element.

Prop

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

DialogClose

Supports all Button props in addition to its own. Renders a <button> element.

Prop

addonAfter

Display content inside the button after children.

ReactNode

addonBefore

Display content inside the button before children.

ReactNode

appearance

Control the appearance by selecting between the different button types.

"default" | "danger" | "primary" | "subtle" | "danger-outline" | "inverse"

Default: subtle

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

disabled

Whether the button is disabled.

false | true

icon

Display an icon before or after the button content or omit children to only show the icon.

ReactNode

iconPosition

Control whether to show the icon before or after the button content.

"end" | "start"

loading

Whether to show loading spinner inside the button.

false | true

size

Control the size of the button.

"sm" | "md" | "lg"

square

Whether button should have square shape.

false | true

Changelog

0.1.0

  • Added component
Last updated on