Skip to main content

Dialog

Overview

The Dialog component provides ready-made confirm and alert dialogs, so a destructive action stays one await dialog.confirm() call away.

Dialogs are modal and require user interaction before they can be dismissed.


Import

import { Dialog, DialogContainer, dialog } from '@allxsmith/bestax-bulma';

Usage

Alert Dialog

A simple alert dialog with only a confirm button.

function example() {
  const [showDialog, setShowDialog] = useState(false);
  return (
    <Block>
      <Button color="info" onClick={() => setShowDialog(true)}>
        Show Alert
      </Button>
      <Dialog
        isOpen={showDialog}
        title="Information"
        message="Your changes have been saved successfully."
        type="success"
        showCancel={false}
        onConfirm={() => setShowDialog(false)}
      />
    </Block>
  );
}


Confirm Dialog

A confirmation dialog with both confirm and cancel options.

function example() {
  const [showDialog, setShowDialog] = useState(false);
  const [result, setResult] = useState('');

  return (
    <Block>
      <Button color="danger" onClick={() => setShowDialog(true)}>
        Delete Item
      </Button>
      <Paragraph mt="2">Result: {result}</Paragraph>
      <Dialog
        isOpen={showDialog}
        title="Delete Item?"
        message="This action cannot be undone. Are you sure you want to delete this item?"
        type="danger"
        confirmText="Delete"
        onConfirm={() => {
          setResult('Deleted');
          setShowDialog(false);
        }}
        onCancel={() => {
          setResult('Cancelled');
          setShowDialog(false);
        }}
      />
    </Block>
  );
}


Dialog Types

Different dialog types with matching icons.

function example() {
  const [dialogType, setDialogType] = useState(null);
  return (
    <Block>
      <Buttons>
        <Button color="success" onClick={() => setDialogType('success')}>
          Success
        </Button>
        <Button color="danger" onClick={() => setDialogType('danger')}>
          Danger
        </Button>
        <Button color="warning" onClick={() => setDialogType('warning')}>
          Warning
        </Button>
        <Button color="info" onClick={() => setDialogType('info')}>
          Info
        </Button>
      </Buttons>
      {dialogType && (
        <Dialog
          isOpen
          title={`${dialogType.charAt(0).toUpperCase() + dialogType.slice(1)} Dialog`}
          message={`This is a ${dialogType} dialog with an automatic icon.`}
          type={dialogType}
          onConfirm={() => setDialogType(null)}
          onCancel={() => setDialogType(null)}
        />
      )}
    </Block>
  );
}


With Rich Content

Dialog with custom React content.

function example() {
  const [showDialog, setShowDialog] = useState(false);
  return (
    <Block>
      <Button onClick={() => setShowDialog(true)}>Show Terms</Button>
      <Dialog
        isOpen={showDialog}
        title="Terms of Service"
        message={
          <Block>
            <Paragraph mb="2">By clicking "Accept", you agree to:</Paragraph>
            <ul>
              <li>Our terms of service</li>
              <li>Our privacy policy</li>
              <li>Receive email notifications</li>
            </ul>
          </Block>
        }
        confirmText="Accept"
        cancelText="Decline"
        onConfirm={() => setShowDialog(false)}
        onCancel={() => setShowDialog(false)}
      />
    </Block>
  );
}


Non-cancelable Dialog

A dialog that must be confirmed (cannot be dismissed by clicking outside or pressing Escape).

function example() {
  const [showDialog, setShowDialog] = useState(false);
  return (
    <Block>
      <Button color="warning" onClick={() => setShowDialog(true)}>
        Show Required Action
      </Button>
      <Dialog
        isOpen={showDialog}
        title="Required Action"
        message="You must complete this action to continue."
        type="warning"
        canCancel={false}
        showCancel={false}
        confirmText="I Understand"
        onConfirm={() => setShowDialog(false)}
      />
    </Block>
  );
}


Portal

Set portal to render the dialog into document.body instead of inline — it's forwarded straight to the underlying Modal, which renders inline on the server and during hydration, then moves into the portal once the client takes over so hydration matches.

function example() {
  const [showDialog, setShowDialog] = useState(false);
  return (
    <Block>
      <Button onClick={() => setShowDialog(true)}>Show Portaled Dialog</Button>
      <Dialog
        isOpen={showDialog}
        title="Rendered in document.body"
        message="This dialog is portaled, so it escapes any ancestor with overflow: hidden."
        onConfirm={() => setShowDialog(false)}
        showCancel={false}
        portal
      />
    </Block>
  );
}


Programmatic API

For showing dialogs from anywhere in your app, use the programmatic API.

Setup

Add the DialogContainer once at your app root:

src/App.tsx
import { DialogContainer } from '@allxsmith/bestax-bulma';

function App() {
return (
<>
<YourRoutes />
<DialogContainer />
</>
);
}

Programmatic Alert

function example() {
  return (
    <Block>
      <DialogContainer />
      <Buttons>
        <Button
          color="info"
          onClick={() => dialog.alert('Something happened!')}
        >
          Simple Alert
        </Button>
        <Button
          color="success"
          onClick={() =>
            dialog.alert({
              title: 'Success',
              message: 'Operation completed!',
              type: 'success',
            })
          }
        >
          Success Alert
        </Button>
      </Buttons>
    </Block>
  );
}

Programmatic Confirm

function example() {
  const [result, setResult] = useState('');

  return (
    <Block>
      <DialogContainer />
      <Button
        color="danger"
        onClick={async () => {
          const confirmed = await dialog.confirm({
            title: 'Delete Item?',
            message: 'This action cannot be undone.',
            type: 'danger',
            confirmText: 'Delete',
          });
          setResult(confirmed ? 'Item deleted!' : 'Cancelled.');
        }}
      >
        Delete Item
      </Button>
      {result && <Paragraph mt="3">{result}</Paragraph>}
    </Block>
  );
}

Chained Dialogs

function example() {
  return (
    <Block>
      <DialogContainer />
      <Button
        color="warning"
        onClick={async () => {
          const confirmed = await dialog.confirm({
            title: 'Delete Item?',
            message: 'This action cannot be undone.',
            type: 'danger',
            confirmText: 'Delete',
          });
          if (confirmed) {
            await dialog.alert({
              title: 'Deleted',
              message: 'Item was deleted successfully.',
              type: 'success',
            });
          }
        }}
      >
        Delete with Confirmation
      </Button>
    </Block>
  );
}


Accessibility

  • Uses role="alertdialog" for proper screen reader announcement
  • Has aria-modal="true" to indicate modal behavior — note this does not make background content inert; no inert attribute is applied, so background content stays reachable by pointer
  • title is wired up as aria-labelledby and message as aria-describedby
  • Focus moves to the confirm (or cancel, with focusCancel) button when opened
  • Escape key closes the dialog (when canCancel is true) — handled by the underlying Modal, which routes it to the topmost open modal only
  • Tab and Shift+Tab cycle within the dialog while it is open — also from the underlying Modal
  • Body scroll is prevented when dialog is open, through the ref-counted lock shared with Modal, Sidebar and Loading
  • Confirm/cancel buttons are keyboard accessible

  • Toast - For non-blocking notifications with optional action buttons
  • Modal - For custom modal dialogs

Additional Resources

Pro Tip

Use the programmatic dialog.confirm() with async/await to create clean, sequential flows without managing dialog state manually.


Props

PropTypeDefaultDescription
isOpenbooleanWhether the dialog is open (required).
titlestringDialog title.
messagestring | React.ReactNodeDialog message/content (required).
type'default' | 'success' | 'danger' | 'warning' | 'info''default'The type/color of the dialog. Default: 'default'.
confirmTextstring'OK'Text for confirm button. Default: 'OK'.
cancelTextstring'Cancel'Text for cancel button. Default: 'Cancel'.
onConfirm() => voidCallback when confirm button is clicked.
onCancel() => voidCallback when cancel button is clicked or dismissed.
showCancelbooleantrueWhether to show cancel button. Default: true for confirm dialogs.
canCancelbooleantrueWhether the dialog can be dismissed. Default: true.
focusCancelbooleanfalseFocus cancel button instead of confirm. Default: false.
iconReact.ReactNodeCustom icon to display.
portalboolean | string | HTMLElementfalseRenders the dialog into a portal target instead of inline. Forwarded to the underlying Modal; see its portal prop for the accepted values.
classNamestringAdditional CSS classes.
refReact.Ref<HTMLElement>Ref forwarded to the dialog element.
...All standard <div> attributes and Bulma helper propsSee Helper Props

CSS & Sass Variables

Dialog registers these variables on its own .dialog element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-dialog-width$dialog-width420px
--bulma-dialog-max-width$dialog-max-width90%
--bulma-dialog-radius$dialog-radiusvar(--bulma-radius)
--bulma-dialog-background$dialog-backgroundvar(--bulma-scheme-main)
--bulma-dialog-shadow$dialog-shadow0 8px 24px hsla(0, 0%, 0%, 0.2)
--bulma-dialog-header-padding$dialog-header-padding1rem 1.25rem
--bulma-dialog-body-padding$dialog-body-padding1.25rem
--bulma-dialog-body-color$dialog-body-colorvar(--bulma-text)
--bulma-dialog-body-line-height$dialog-body-line-height1.5
--bulma-dialog-footer-padding$dialog-footer-padding1rem 1.25rem
--bulma-dialog-footer-gap$dialog-footer-gap0.75rem
--bulma-dialog-border-color$dialog-border-colorvar(--bulma-border)
--bulma-dialog-title-size$dialog-title-sizevar(--bulma-size-5)
--bulma-dialog-title-weight$dialog-title-weightvar(--bulma-weight-semibold)
--bulma-dialog-title-color$dialog-title-colorvar(--bulma-text-strong)
--bulma-dialog-icon-size$dialog-icon-size1.5rem
--bulma-dialog-icon-margin$dialog-icon-margin0.75rem
--bulma-dialog-animation-duration$dialog-animation-duration0.2s