Modal

The SDK exports two modal components:

  1. Modal (legacy)
  2. GlobalModal (introduced with stream-chat-react@13.6.0)

Best Practices

  • Prefer GlobalModal to avoid clipping in complex layouts.
  • Keep modal content lightweight to prevent scroll jank.
  • Use onCloseAttempt to enforce confirmation flows when needed.
  • Ensure open state is controlled by a single source of truth.
  • Verify keyboard and overlay close behavior for accessibility.

Modals use position: fixed. The legacy Modal renders deep in the tree (for example under Message), which in Safari can cause clipping if a parent uses position: relative. To avoid this, use GlobalModal, which renders at the top of the SDK tree.

By default, the SDK uses Modal. Opt in to GlobalModal by passing it to the Channel Modal prop:

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

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

Both modals accept the same props.

children

The children prop is used to render the modal content.

import { useCallback, useState } from "react";

const Component = ({ image }) => {
  const [modalIsOpen, setModalIsOpen] = useState(false);
  const closeModal = useCallback(() => {
    setModalIsOpen(false);
  }, []);

  return (
    <>
      <button onClick={() => setModalIsOpen(true)}>Open gallery</button>
      <Modal
        className="str-chat__image-modal"
        onClose={closeModal}
        open={modalIsOpen}
      >
        <ModalGallery images={[image]} index={0} />
      </Modal>
    </>
  );
};

open

Flag to determine whether the modal and its content is rendered.

TypeDefault
booleanfalse

className

Class attached to the modal overlay div with default class str-chat__modal.

Type
string

onClose

onClose should update open to hide the modal (typically set to false).

Type
(event: ModalCloseEvent) => void

Where ModalCloseEvent is defined as follows:

export type ModalCloseEvent =
  | KeyboardEvent
  | React.KeyboardEvent
  | React.MouseEvent<HTMLButtonElement | HTMLDivElement>;

onCloseAttempt

Optional handler to intercept closing logic. Return false to prevent onClose. onCloseAttempt recognizes:

  1. overlay - click on overlay
  2. button - click on close button
  3. escape - pressing Escape key

By default, modals are closed upon interacting with all the above sources.

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

// click on overlay will not result in closing the modal
const onCloseAttempt: (source: ModalCloseSource) => boolean = (source) =>
  source !== "overlay";

const GlobalModalWrapper = (props: ModalProps) => {
  return <GlobalModal {...props} onCloseAttempt={onCloseAttempt} />;
};

const Component = () => (
  <Channel Modal={GlobalModalWrapper}>{/*    children*/}</Channel>
);
Type
(source: ModalCloseSource, event: ModalCloseEvent) => boolean