Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Attachments

Attachment renders what a message carries: images, videos, files, audio, voice recordings, Giphy, link previews and a shared location. It sorts the attachments into groups and renders each group with the component its prop names. Attachments and uploads explains how attachments get there and how they show.

Best practices

  • Replace one renderer by passing it to Attachment, and register the wrapper as the Attachment override. The default MessageUI is the only part that reads that key.
  • Pass components and handlers that don't change, declared outside the component. Attachment builds its groups again only when attachments or attachmentActionsDefaultFocus changes.
  • In a custom message UI, pass message.shared_location together with message.attachments. The location isn't in attachments.

Default rendering

Attachment renders the groups in this order:

Group Attachments Rendered by
Media images, type: 'video', and anything with an MP4, Ogg, WebM or QuickTime custom.mime_type Image for one image, Media for one video, ModalGallery for several
Giphy type: 'giphy' Giphy, with AttachmentActions while it's a preview with buttons
Card anything with og_scrape_url or title_link, such as a link preview Card, with AttachmentActions when it has actions
Geolocation the shared location Geolocation
File audio, voice recordings, type: 'file', and anything else with a custom.mime_type Audio, VoiceRecording and File
Unsupported anything else UnsupportedAttachment

A Giphy and a link preview are checked first: an image with og_scrape_url is a card. A file, an audio file or a voice recording with neither a URL nor a local upload renders nothing.

Unsupported attachments

An attachment that fits no other group goes to UnsupportedAttachment: an empty object, or one with no custom.mime_type and a type the SDK doesn't know, such as a type of your own. The default shows an icon and "Unsupported attachment". It takes no props.

Attachment renders the component on this prop with { attachment }, typed UnsupportedAttachmentProps, so a replacement can render types of its own and fall back to the default for the rest. Change how attachments render has an example.

Basic usage

The default MessageUI renders Attachment for you. A custom message UI renders it with the message's attachments and location, and passes handleAction for the buttons of a Giphy preview:

import { Attachment, MessageText, useMessageContext } from "stream-chat-react";

export const MessageWithAttachments = () => {
  const { handleAction, message } = useMessageContext();
  const attachments = [
    ...(message.shared_location ? [message.shared_location] : []),
    ...(message.attachments ?? []),
  ];

  return (
    <div className="message-with-attachments">
      {attachments.length > 0 && (
        <Attachment actionHandler={handleAction} attachments={attachments} />
      )}
      <MessageText />
    </div>
  );
};

UI customization

This wrapper renders files with a component of its own and keeps the other renderers:

import type { AttachmentProps, FileAttachmentProps } from "stream-chat-react";
import { Attachment, WithComponents } from "stream-chat-react";

const FileLink = ({ attachment }: FileAttachmentProps) => (
  <a download href={attachment.asset_url} rel="noreferrer" target="_blank">
    {attachment.title ?? "Download file"}
  </a>
);

const AttachmentWithFileLinks = (props: AttachmentProps) => (
  <Attachment {...props} File={FileLink} />
);

const overrides = { Attachment: AttachmentWithFileLinks };

export const App = () => (
  <WithComponents overrides={overrides}>
    {/* the rest of the app */}
  </WithComponents>
);

The default Image renders the ModalGallery override key with one item, not the ModalGallery prop. To change both single images and galleries, set the key. The BaseImage, ImagePlaceholder, VideoPlayer, AttachmentFileIcon and AttachmentUploadProgressIndicator keys change parts inside the renderers; see BaseImage and ComponentContext.

Image and video sizing

The CSS variable --str-chat__attachment-max-width sets the largest width and height of images and videos. It must resolve to pixels in getComputedStyle, for example 300px, 10rem or calc(300px - var(--margin)). When it doesn't, the SDK can't size attachments, logs a warning, and the message list can scroll to the wrong place.

The default size handlers ask Stream's CDN for a copy that fits:

  1. They read the original size from the oh and ow parameters of the attachment's URL.
  2. They read the element's max-width, and its max-height or height, in pixels.
  3. They add h and w parameters for twice that size, for high-density screens.

For a video, the same happens to thumb_url, and only while shouldGenerateVideoThumbnail is on. The video itself loads from asset_url.

When your files aren't on Stream's CDN, give Attachment handlers of your own. This one asks an image service for a copy twice the element's maximum width:

import type {
  AttachmentProps,
  ImageAttachmentSizeHandler,
} from "stream-chat-react";
import { Attachment, WithComponents } from "stream-chat-react";

const imageFromMyCdn: ImageAttachmentSizeHandler = (attachment, element) => {
  const source = attachment.image_url ?? attachment.thumb_url ?? "";
  const maxWidth = parseFloat(getComputedStyle(element).maxWidth) || 300;
  const width = Math.round(maxWidth * 2);

  return {
    url: `https://images.example.com/resize?width=${width}&src=${encodeURIComponent(source)}`,
  };
};

const AttachmentFromMyCdn = (props: AttachmentProps) => (
  <Attachment {...props} imageAttachmentSizeHandler={imageFromMyCdn} />
);

const overrides = { Attachment: AttachmentFromMyCdn };

export const App = () => (
  <WithComponents overrides={overrides}>
    {/* the rest of the app */}
  </WithComponents>
);

For an image or a Giphy, an empty url keeps the attachment's own URL. A video handler has to return the video's url, as the default does with asset_url. The components inside Attachment read these settings from AttachmentContext.

Props

Prop Type Default Description
attachments (Attachment | SharedLocationResponseData)[] required The attachments to render, the shared location included.
actionHandler (dataOrName?: string | FormData, value?: string, event?: BaseSyntheticEvent) => Promise<void> | void Called by the attachments' action buttons, such as a Giphy preview's send, shuffle and cancel. Pass handleAction from the context.
AttachmentActions ComponentType<AttachmentActionsProps> AttachmentActions Renders an attachment's action buttons.
attachmentActionsDefaultFocus AttachmentActionsDefaultFocusByType { giphy: 'send' } Which action button gets the focus, by attachment type. Pass {} to focus none.
Audio ComponentType<AudioProps> Audio Renders an audio file.
Card ComponentType<CardProps> Card Renders a link preview. Receives the attachment's fields as props.
File ComponentType<FileAttachmentProps> FileAttachment Renders a file.
Geolocation ComponentType<GeolocationProps> Geolocation Renders the shared location.
Giphy ComponentType<{ attachment: Attachment }> the SDK's Giphy component Renders a Giphy. The default isn't exported.
giphyVersion GiphyVersions 'fixed_height' The Giphy rendition to show, in Giphy attachments and cards.
Image ComponentType<ImageProps> ImageComponent Renders a single image. Receives the image as a gallery item.
imageAttachmentSizeHandler (attachment: Attachment, element: HTMLElement) => { url: string } the CDN handler above Picks the URL to load for an image and for a Giphy, from the rendered element.
isQuoted boolean false Renders voice recordings in the compact form of a quoted message.
Media ComponentType<{ controls?: boolean; isPlaying?: boolean; thumbnailUrl?: string; videoUrl?: string }> the SDK's video player Plays a single video. The default uses the VideoPlayer key when set, else loads react-player on demand.
ModalGallery ComponentType<ModalGalleryProps> ModalGallery Renders several images and videos as thumbnails that open a full-screen gallery.
shouldGenerateVideoThumbnail boolean true Shows a video's thumb_url with a play button until the user plays it. With false, the player shows at once.
UnsupportedAttachment ComponentType<UnsupportedAttachmentProps> UnsupportedAttachment Renders an attachment no other group takes. Receives { attachment }.
videoAttachmentSizeHandler (attachment: Attachment, element: HTMLElement, shouldGenerateVideoThumbnail: boolean) => { thumbUrl?: string; url: string } the CDN handler above Picks the URLs to load for a video and its thumbnail.
VoiceRecording ComponentType<VoiceRecordingProps> VoiceRecording Renders a voice recording. See Voice Recording Attachment.

The handler types are exported as ImageAttachmentSizeHandler and VideoAttachmentSizeHandler.

Hooks

  • useAttachmentContext() returns giphyVersion, the two size handlers and shouldGenerateVideoThumbnail, for a renderer of your own. See AttachmentContext.
  • useAttachmentUploadState(attachment) returns isUploading, progress and uploadConfirmationPending, for a renderer that shows an upload in progress.
  • useMessageContext() gives handleAction and the message.