ComponentsDialog

Dialog

Display a modal dialog box.

Documentation

Usage

import {
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@optiaxiom/react";
 
export function App() {
  return (
    <Dialog>
      <DialogTrigger>Open Dialog</DialogTrigger>
 
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Modal Title</DialogTitle>
        </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,
  DialogTitle,
  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>
          <DialogTitle>Modal Title</DialogTitle>
        </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,
  DialogTitle,
  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>
          <DialogTitle>Modal Title</DialogTitle>
        </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 DialogDescription component.

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@optiaxiom/react";
 
export function App() {
  return (
    <Dialog>
      <DialogTrigger>Open Dialog</DialogTrigger>
 
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Modal Title</DialogTitle>
          <DialogDescription>
            This is additional description of the modal
          </DialogDescription>
        </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,
  DialogTitle,
  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>
          <DialogTitle>Modal Title</DialogTitle>
        </DialogHeader>
        <DialogBody>This is the modal body</DialogBody>
        <DialogFooter>
          <DialogClose appearance="primary">Close</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

Form dialogs

Wrap dialog content with the DialogForm component to use forms in dialogs.

import {
  Button,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogForm,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  Field,
  Flex,
  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">
        <DialogForm
          onSubmit={(event) => {
            event.preventDefault();
            setLoading(true);
            // use form data to perform a server action
            setTimeout(() => {
              setLoading(false);
              setOpen(false);
            }, 3000);
          }}
        >
          <DialogHeader>
            <DialogTitle>Create new issue</DialogTitle>
          </DialogHeader>
 
          <DialogBody>
            <Flex>
              <Field label="Title">
                <Input name="title" required />
              </Field>
              <Field label="Description">
                <Textarea name="description" required />
              </Field>
            </Flex>
          </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,
  DialogTitle,
  DialogTrigger,
} from "@optiaxiom/react";
 
export function App() {
  return (
    <Dialog>
      <DialogTrigger>Open Dialog</DialogTrigger>
 
      <DialogContent size="sm">
        <DialogHeader>
          <DialogTitle>Modal Title</DialogTitle>
        </DialogHeader>
 
        <DialogBody>This is the modal body</DialogBody>
 
        <DialogFooter>
          <Dialog>
            <DialogTrigger mr="auto">Open nested dialog</DialogTrigger>
 
            <DialogContent size="sm">
              <DialogHeader>
                <DialogTitle>Modal Title</DialogTitle>
              </DialogHeader>
 
              <DialogBody>This is the modal body</DialogBody>
 
              <DialogFooter>
                <Dialog>
                  <DialogTrigger mr="auto">Open nested dialog</DialogTrigger>
 
                  <DialogContent size="sm">
                    <DialogHeader>
                      <DialogTitle>Modal Title</DialogTitle>
                    </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.

💡

Type something into the text field and then attempt to close the dialog:

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogBody,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogFooter,
  AlertDialogHeader,
  Button,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogForm,
  DialogHeader,
  DialogTitle,
  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">
        <DialogForm
          onSubmit={(event) => {
            event.preventDefault();
            setOpen(false);
          }}
        >
          <DialogHeader>
            <DialogTitle>Create new issue</DialogTitle>
          </DialogHeader>
 
          <DialogBody>
            <Textarea 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 />
            <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

iconOnly

Whether button should have square shape.

false | true

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"

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"

Default: fade

DialogHeader

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

DialogTitle

Supports all Heading props in addition to its own. Renders an <h2> 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

level

Switch between the different h1-h6 levels.

"1" | "2" | "3" | "4" | "6" | "5"

lineClamp

Truncate the text at specific number of lines.

"1" | "2" | "3" | "4"

truncate

Whether to truncate the text and add an ellipsis at the end.

false | true

DialogActions

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

DialogDescription

Supports all Text props in addition to its own. Renders a <p> 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"

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

iconOnly

Whether button should have square shape.

false | true

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"

Default: lg

Changelog

0.1.0

  • Added component