This is beta documentation for Stream Chat React SDK v14. For the latest stable version, see the latest version (v13) .

Dialog Management

Dialog management in the React SDK covers anchored popups such as menus and callouts, plus full-screen modal surfaces such as GlobalModal.

Best Practices

  • Use DialogAnchor for anchored popups and GlobalModal for modal content.
  • Keep dialog IDs stable so focus and open state are preserved across renders.
  • Read open state through useDialogIsOpen for accurate ARIA attributes.
  • Use the nearest dialog manager when integrating with SDK surfaces such as AttachmentSelector, reaction selectors, and message actions.
  • Keep popup content small and delegate larger flows to prompts or modals.

Anchored Dialogs

Anchored dialogs are positioned relative to a reference element and rendered through a dialog manager.

Rendering a dialog

import { useRef } from "react";
import { DialogAnchor, useDialog, useDialogIsOpen } from "stream-chat-react";

const dialogId = "custom-help-dialog";

const HelpMenu = () => {
  const buttonRef = useRef<HTMLButtonElement | null>(null);
  const dialog = useDialog({ id: dialogId });
  const dialogIsOpen = useDialogIsOpen(dialogId);

  return (
    <>
      <button
        aria-expanded={dialogIsOpen}
        onClick={() => dialog.toggle()}
        ref={buttonRef}
      >
        Toggle help
      </button>
      <DialogAnchor
        id={dialogId}
        placement="top-start"
        referenceElement={buttonRef.current}
        trapFocus
      >
        <div className="custom-help-dialog">Help content</div>
      </DialogAnchor>
    </>
  );
};

Dialog API

useDialog() returns a dialog controller with:

  • open()
  • close()
  • toggle()
  • remove()

Use useDialogIsOpen(id) to subscribe to open-state changes and wire aria-expanded or other UI state.

Dialog Managers

Anchored dialogs render through the nearest DialogManagerProvider. SDK surfaces such as MessageInput, MessageList, and Chat already create the dialog-manager layers they need.

If you build a custom popup subtree outside those surfaces, add your own manager:

import { DialogManagerProvider } from "stream-chat-react";

const CustomDialogArea = ({ children }) => (
  <DialogManagerProvider id="custom-dialog-manager">
    {children}
  </DialogManagerProvider>
);

Callouts, Context Menus, Prompts, And Alerts

The SDK ships higher-level dialog primitives built on the same manager system:

  • Callout
  • ContextMenu
  • Prompt
  • Alert

Use them when your custom UI matches one of those patterns instead of rebuilding focus, placement, and dismissal logic yourself.

GlobalModal is the modal primitive used for full-screen or overlay flows. Chat already mounts the modal dialog manager required by GlobalModal.

import { useCallback, useState } from "react";
import { GlobalModal } from "stream-chat-react";

const Example = () => {
  const [open, setOpen] = useState(false);
  const close = useCallback(() => setOpen(false), []);

  return (
    <>
      <button onClick={() => setOpen(true)}>Open modal</button>
      <GlobalModal onClose={close} open={open}>
        <div className="custom-modal-body">Modal content</div>
      </GlobalModal>
    </>
  );
};

For SDK-owned modal flows such as attachment-selector dialogs or delete confirmations, override the shared Modal component with WithComponents:

import {
  Channel,
  GlobalModal,
  MessageInput,
  WithComponents,
} from "stream-chat-react";
import type { ModalProps } from "stream-chat-react";

const CustomModal = (props: ModalProps) => (
  <GlobalModal {...props} className="custom-modal-shell" />
);

const App = () => (
  <WithComponents overrides={{ Modal: CustomModal }}>
    <Channel>
      <MessageInput />
    </Channel>
  </WithComponents>
);