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<
    'success' | 'danger' | 'warning' | 'info' | null
  >(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 through a portal 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="Portaled Dialog"
        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
isOpenboolean—Whether the dialog is open (required).
titlestring—Dialog title.
messagestring | React.ReactNode—Dialog 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() => void—Callback when confirm button is clicked.
onCancel() => void—Callback 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.ReactNode—Custom 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.
classNamestring—Additional CSS classes.
refReact.Ref<HTMLElement>—Ref forwarded to the dialog element.
...All standard <div> attributes and Bulma helper props—See 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