Dialog Management

Dialog management in the React SDK controls:

  • dialog popups like menus (ReactionSelector, MessageActionsBox, AttachmentSelector, ShareLocationDialog)
  • modals (if GlobalModal is used)

Best Practices

  • Use DialogAnchor for popups and GlobalModal for full modals.
  • Keep dialog IDs stable to avoid losing state across renders.
  • Use useDialogIsOpen for accurate ARIA aria-expanded states.
  • Close or remove dialogs on unmount to prevent leaks.
  • Avoid stacking multiple dialogs unless the UX requires it.

Setup dialog popup display

There are two parts: the component that requests open/close, and the component that renders the dialog. Start by rendering a component in a dialog.

Rendering a dialog

Wrap the component you want rendered as a floating dialog inside DialogAnchor:

import React, { ElementRef, useRef } from "react";
import { DialogAnchor } from "stream-chat-react";

import { ComponentToDisplayOnDialog } from "./ComponentToDisplayOnDialog";
import { generateUniqueId } from "./generateUniqueId";

const Container = () => {
  // DialogAnchor needs a reference element to position the dialog
  const buttonRef = useRef<ElementRef<"button">>(null);
  // the dialog ID lets you access it via DialogManagerProviderContext
  const dialogId = generateUniqueId();

  return (
    <>
      <DialogAnchor
        id={dialogId}
        placement="top"
        referenceElement={buttonRef.current}
        trapFocus
      >
        <ComponentToDisplayOnDialog />
      </DialogAnchor>
    </>
  );
};

Controlling a dialog's display

The dialog display is controlled via Dialog API. You can access the API via useDialog() hook.

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

import { ComponentToDisplayOnDialog } from "./ComponentToDisplayOnDialog";
import { generateUniqueId } from "./generateUniqueId";

const Container = () => {
  const buttonRef = useRef<ElementRef<"button">>(null);
  const dialogId = generateUniqueId();
  // access the dialog controller which provides the dialog API
  const dialog = useDialog({ id: dialogId });
  // subscribe to dialog open state changes
  const dialogIsOpen = useDialogIsOpen(dialogId);

  return (
    <>
      <DialogAnchor
        id={dialogId}
        placement="top"
        referenceElement={buttonRef.current}
        trapFocus
      >
        <ComponentToDisplayOnDialog />
      </DialogAnchor>
      <button
        aria-expanded={dialogIsOpen}
        onClick={() => dialog.toggle()}
        ref={buttonRef}
      >
        Toggle
      </button>
    </>
  );
};

Dialog API

Control a dialog via the Dialog object from useDialog():

  • dialog.open() - opens the dialog
  • dialog.close() - closes the dialog
  • dialog.toggle() - toggles open state. Accepts closeAll to close other open dialogs.
  • dialog.remove() - removes the dialog object reference from the state (primarily for cleanup purposes)

Every Dialog object carries its own id and isOpen flag.

Dialog utility hooks

There are the following utility hooks that can be used to subscribe to state changes or access a given dialog:

  • useDialogIsOpen(id: string) - allows to observe the open state of a particular Dialog instance
  • useDialog({ id }: GetOrCreateDialogParams) - retrieves a dialog object that exposes API to manage it
  • useOpenedDialogCount() - allows to observe changes in the open dialog count

Custom DialogManagerProvider Context

To render dialogs outside MessageList/VirtualizedMessageList, create a DialogManagerProvider context.

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

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

Now the children of DialogAnchor will be anchored to the parent DialogManagerProvider.

Render Modal with Dialog API

Rendering modals with the Dialog API places them at the top of the Chat tree and avoids UI glitches from CSS positioning (for example, Safari clipping position: fixed under a relative parent).

To start rendering the modal at the top of the chat tree, it is necessary to use GlobalModal for modal rendering:

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

const Component = () => <Channel Modal={GlobalModal}></Channel>;

Then render the modal like this:

import { ModalGallery, useComponentContext } from "stream-chat-react";

const Component = () => {
  const { Modal } = useComponentContext();

  return (
    <>
      <button>Open Modal</button>
      {/* Modal will be renderd via React portal at the top of the tree */}
      <Modal
        className="modal-gallery-modal"
        onClose={closeModal}
        open={modalIsOpen}
      >
        <ModalGallery images={[props]} index={0} />
      </Modal>
    </>
  );
};