# 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`:

```tsx
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.

```tsx
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.

```tsx
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:

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

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

Then render the modal like this:

```js
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>
    </>
  );
};
```


---

This page was last updated at 2026-04-21T07:55:48.152Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v13/guides/dialog-management/](https://getstream.io/chat/docs/sdk/react/v13/guides/dialog-management/).