import { Attachment, MessageText, useMessageContext } from "stream-chat-react";
const CustomMessage = () => {
const { message } = useMessageContext();
const attachments = [
...(message.shared_location ? [message.shared_location] : []),
...(message.attachments ?? []),
];
return (
<div>
<MessageText />
{attachments.length ? <Attachment attachments={attachments} /> : null}
</div>
);
};Attachments
Attachment renders the attachments for a message and chooses the UI based on each attachment shape.
Best Practices
- Keep custom attachment logic focused on the types you actually use.
- Prefer the supported override points on
Attachmentinstead of customizing low-level attachment containers directly. - Combine
message.shared_locationwithmessage.attachmentswhen you renderAttachmentinside a fully custom message UI. - Use CDN-backed sizing and the SDK sizing handlers for images and videos whenever possible.
- Treat native
giphymessages as their own attachment surface; they render throughGiphy, notModalGallery. - Test messages with mixed attachment types so your custom layout still behaves well.
Default Rendering
Attachment groups message attachments before rendering them. The default UI surface is:
| Attachment shape | Default UI |
|---|---|
| audio attachment | Audio |
| scraped content / OG attachment | Card |
| file attachment | FileAttachment |
| giphy attachment | Giphy |
| shared location | Geolocation |
| single image attachment | Image |
| single video attachment | VideoPlayer |
| multiple image/video attachments | ModalGallery |
| voice recording attachment | VoiceRecording |
| unsupported attachment | UnsupportedAttachment |
Attachments with og_scrape_url or title_link are rendered as scraped content cards. Shared location is read from message.shared_location, not from message.attachments.
Native giphy attachments render inline through Giphy. They do not open ModalGallery; the gallery viewer is used for image and mixed image/video gallery content. If you want a modal or fullscreen experience for sent giphies, override Giphy directly.
Basic Usage
The default SDK message UI already renders Attachment for you. If you build your own message component, render it with the current message attachments:
UI Customization
The supported media override points are:
Imagefor a single image attachmentMediafor a single video attachmentModalGalleryfor multi-image or mixed image/video galleries
You can also replace AttachmentActions, Audio, Card, File, Geolocation, Giphy, UnsupportedAttachment, and VoiceRecording.
Customizing Attachment inside your own message UI
If you render Attachment directly, pass the custom subcomponents you need:
import {
Attachment,
type AttachmentProps,
type ModalGalleryProps,
type VideoPlayerProps,
} from "stream-chat-react";
const CustomVideoPlayer = ({ thumbnailUrl, videoUrl }: VideoPlayerProps) => (
<video controls poster={thumbnailUrl} src={videoUrl} />
);
const CustomModalGallery = ({ items }: ModalGalleryProps) => (
<div className="custom-gallery-grid">
{items.map((item, index) => {
const previewUrl = item.imageUrl ?? item.videoThumbnailUrl;
if (!previewUrl) return null;
return (
<button key={index}>
<img alt={item.alt ?? "attachment"} src={previewUrl} />
</button>
);
})}
</div>
);
const CustomAttachment = (props: AttachmentProps) => (
<Attachment
{...props}
Media={CustomVideoPlayer}
ModalGallery={CustomModalGallery}
/>
);Replacing the SDK attachment renderer
If you want the SDK Message, VirtualMessage, and thread surfaces to use your attachment renderer, register it with WithComponents:
import {
Channel,
MessageInput,
MessageList,
WithComponents,
} from "stream-chat-react";
const App = () => (
<WithComponents overrides={{ Attachment: CustomAttachment }}>
<Channel>
<MessageList />
<MessageInput />
</Channel>
</WithComponents>
);If you customized low-level attachment containers in earlier versions, prefer these v14-supported surfaces first: Attachment, Image, Media, ModalGallery, File, Card, Giphy, Geolocation, VoiceRecording, and AttachmentActions.
Image And Video Sizing
The SDK computes image and video dimensions from CSS and the channel sizing handlers.
Maximum size
Use --str-chat__attachment-max-width to control the maximum width and height used by image and video attachments. The value must resolve to pixels through getComputedStyle, for example 300px, 10rem, or calc(300px - var(--margin)).
If the value does not resolve to a pixel value, attachment sizing can break and message-list scroll behavior can become inaccurate.
File size optimization
The default image and video renderers use the attachment dimensions together with Stream's CDN so the SDK can request a smaller asset when possible.
Aspect ratio
The SDK preserves aspect ratio when it can, but cropping can still happen when:
- the message layout forces the attachment to fill the available width
- Safari stretches portrait media to the configured max width
- the host element's size constraints cannot fit the source without cropping
If you use your own CDN or custom media sizing rules, provide your own imageAttachmentSizeHandler and videoAttachmentSizeHandler on Channel.
Props
| Prop | Description | Type |
|---|---|---|
actionHandler | Attachment action handler. | (dataOrName?: string | FormData, value?: string, event?: React.BaseSyntheticEvent) => Promise<void> |
AttachmentActions | Component used to render attachment actions. Defaults to AttachmentActions. | component |
attachmentActionsDefaultFocus | Controls the default focused action per attachment type. Defaults to { giphy: "send" }. | AttachmentActionsDefaultFocusByType |
attachments | Message attachments to render. | (Attachment | SharedLocationResponse)[] |
Audio | Component used to render audio attachments. Defaults to Audio. | component |
Card | Component used to render link-preview cards. Defaults to Card. | component |
File | Component used to render file attachments. Defaults to FileAttachment. | component |
Geolocation | Component used to render geolocation attachments. Defaults to Geolocation. | component |
Giphy | Component used to render giphy attachments. Defaults to Giphy. | component |
Image | Component used to render image attachments. Defaults to Image. | component |
isQuoted | Renders the attachment in quoted-message mode. | boolean |
Media | Component used to render media attachments. Defaults to VideoPlayer. | component |
ModalGallery | Component used to render the attachment gallery modal. Defaults to ModalGallery. | component |
UnsupportedAttachment | Component used to render unsupported attachments. Defaults to UnsupportedAttachment. | component |
VoiceRecording | Component used to render voice-recording attachments. Defaults to VoiceRecording. | component |