AI
NUI logov3.1.0

Dialog & DialogProvider

A promise-based imperative dialog system for confirmations, alerts, and critical user interactions. Rather than cluttering your component trees with repetitive useState boilerplate, DialogProvider lets you invoke asynchronous modal dialogs imperatively with await nui.confirm() and await nui.alert().

tsx
import { DialogProvider, nui } from '@nofinite/nui';

Interactive Preview


Architecture Overview

The NUI Dialog architecture decouples the dialog triggering logic from the React rendering tree:

plaintext
┌───────────────────────────────┐
│ Event Handler / Server Action │
│   await nui.confirm(...)      │
└───────────────┬───────────────┘
                │ Dispatches via external store
                ▼
┌───────────────────────────────┐
│   <DialogProvider /> (Root)   │
│   Mounts Modal & Traps Focus  │
└───────────────┬───────────────┘
                │ Resolves Promise on User Action
                ▼
┌───────────────────────────────┐
│  isConfirmed === true / false  │
└───────────────────────────────┘
  1. Mount <DialogProvider /> Once: Place at your application root (e.g. layout.tsx).
  2. Call Anywhere: Trigger nui.confirm(...) or nui.alert(...) anywhere in client handlers, async utilities, or data-fetching interceptors.
  3. Promise-Driven Flow: Await the user's decision without managing local open/close state.

Root Setup

Mount <DialogProvider /> once in your root layout:

tsx
// app/layout.tsx (Next.js App Router)
import { DialogProvider } from '@nofinite/nui';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <DialogProvider />
      </body>
    </html>
  );
}

Dialog Types & Usage

Destructive Confirm Dialog

Use nui.confirm() to ask users for explicit confirmation before carrying out irreversible actions:

tsx
'use client';
import { nui, Button } from '@nofinite/nui';

export function DeleteAccountButton() {
  const handleDelete = async () => {
    const isConfirmed = await nui.confirm(
      'Are you absolutely sure? All data associated with this account will be permanently erased.',
      {
        title: 'Delete Account',
        confirmText: 'Yes, Delete',
        cancelText: 'Cancel',
        isDanger: true,
      }
    );

    if (isConfirmed) {
      // Proceed with deletion API call
      nui.success('Account deleted successfully.');
    } else {
      nui.toast('Deletion cancelled.');
    }
  };

  return (
    <Button variant="danger" onClick={handleDelete}>
      Delete Account
    </Button>
  );
}

Informational Alert Dialog

Use nui.alert() when the user must acknowledge important information or terms before proceeding:

tsx
'use client';
import { nui, Button } from '@nofinite/nui';

export function TermsDialogButton() {
  const handleShowTerms = async () => {
    await nui.alert(
      'By clicking continue, you agree to our updated Terms of Service and Privacy Policy.',
      {
        title: 'Terms of Service Update',
        confirmText: 'I Understand',
      }
    );
    console.log('Terms acknowledged');
  };

  return (
    <Button variant="outline" onClick={handleShowTerms}>
      Review Policy Update
    </Button>
  );
}

Dialog Options Reference

nui.confirm(message, options) and nui.alert(message, options) accept an optional configuration object:

OptionTypeDefaultDescription
titlestring'Confirm' / 'Alert'The heading displayed at the top of the dialog
confirmTextstring'Confirm' / 'OK'Label for the primary action button
cancelTextstring'Cancel'Label for the cancel button (confirm dialogs only)
isDangerbooleanfalseWhen true, renders the primary button with variant="danger"

Keyboard Interaction

KeyBehavior
TabCycles focus between active dialog buttons
Shift + TabReverse cycles focus
EnterActivates the currently focused button
EscapeDismisses alert dialogs (confirm dialogs disable outside dismissal to prevent accidents)

Accessibility

  • Automatically traps keyboard focus within the dialog container while active.
  • Employs semantic dialog roles (role="alertdialog" for destructive confirms).
  • Disables background scrolling and backdrop interaction while open.
  • Restores focus to the triggering element upon dialog dismissal.

Best Practices

Do

  • Mount <DialogProvider /> once at the application root.
  • Use isDanger: true for permanent or destructive flows (deleting databases, removing team members).
  • Pair nui.confirm() with nui.toast() or nui.success() to deliver instant feedback upon resolution.
  • Keep dialog messages clear, unambiguous, and concise.

Don't

  • Do not mount multiple <DialogProvider /> instances across nested routes.
  • Do not trigger dialogs synchronously during component rendering; always invoke within event handlers or effects.
  • Avoid using confirm dialogs for minor or easily undoable operations.

API Reference

DialogProvider PropsHTML Attributes

Inherits all standard attributes and event handlers from { children?: React.ReactNode } — Mount once at app root to enable imperative dialogs (nui.alert, nui.confirm). Supports className for semantic NUICSS superclasses and ref forwarding.