Attachments
A message's attachments render in its bubble, as parts of MessageContent. Message sorts them by kind into the MessageContext values images, videos, files and otherAttachments, and each kind renders in its own part of messageContentOrder: Gallery for images and videos, FileAttachmentGroup for files, and Attachment for each of the others. Every component on this page is an override key of the same name, except UrlPreview, whose default is URLPreview, and FileAttachmentIcon, whose default is FileIcon. Attachments and uploads covers the feature across the layers.
Best practices
- Render a custom attachment type by overriding
Attachment, and hand every other type to the defaultAttachment, as below. Custom attachments builds a complete one. - Replace one kind through its own key, such as
FileAttachmentorUrlPreview, rather thanAttachmentas a whole. - Change what a tap on an attachment does with
onPressMessageonChannel: the payload'semittersays which kind was pressed, andadditionalInfocarries the attachment, the thumbnail or the URL. CalldefaultHandler()to keep the SDK's behavior. - Install a video player, an audio player and a document picker through the native handlers you need. Without a video player, videos render as files; without an audio player, audio attachments do.
Basic usage
A message with an attachment of type product, sent from your backend, renders UnsupportedAttachment by default. This override renders it as a card and hands every other attachment to the default:
import { StyleSheet, Text, View } from "react-native";
import {
Attachment,
type AttachmentProps,
WithComponents,
} from "stream-chat-react-native";
const AttachmentWithProducts = (props: AttachmentProps) => {
const { attachment } = props;
if (attachment?.type === "product") {
return (
<View style={styles.card}>
<Text style={styles.title}>{attachment.title}</Text>
{attachment.text ? <Text>{attachment.text}</Text> : null}
</View>
);
}
return <Attachment {...props} />;
};
const overrides = { Attachment: AttachmentWithProducts };
export const MessagesWithProducts = ({
children,
}: {
children: React.ReactNode;
}) => <WithComponents overrides={overrides}>{children}</WithComponents>;
const styles = StyleSheet.create({
card: { borderRadius: 12, gap: 4, padding: 12, width: 240 },
title: { fontWeight: "600" },
});A custom type lands in otherAttachments, which renders only while the message has no file, image or video attachment.
How attachments are sorted
| Attachment | MessageContext value |
Renders |
|---|---|---|
| an image with an image URL, not a link preview | images |
Gallery |
| a video, not a link preview, with a video player installed | videos |
Gallery, with VideoThumbnail |
| a video without a video player | files |
FileAttachment |
| a file | files |
FileAttachment |
| audio or a voice recording | files |
AudioAttachment, or FileAttachment without an audio player |
an image with og_scrape_url or title_link |
files |
UrlPreview, or URLPreviewCompact with urlPreviewType="compact" |
| Giphy or Imgur | otherAttachments |
Giphy |
| a link preview of another type | otherAttachments |
UrlPreview or URLPreviewCompact |
| any other type | otherAttachments |
UnsupportedAttachment |
A file, image or video attachment empties otherAttachments, so a message holds either those or the others. An image without an image URL isn't rendered.
Attachment
Renders one attachment by its type, in this order: Giphy for a Giphy or Imgur attachment; URLPreviewCompact or UrlPreview for one with og_scrape_url or title_link; Gallery for an image or a playable video; AudioAttachment for audio or a voice recording, with its upload progress while it uploads; FileAttachment for a file; and UnsupportedAttachment otherwise. FileAttachmentGroup renders each file through the Attachment key too.
| Prop | Type | Default | Description |
|---|---|---|---|
attachment |
AttachmentType |
The attachment to render. Without it, nothing renders. | |
index |
number |
Not used, since Attachment doesn't pass it on. |
|
isAttachmentEqual |
(prevAttachment: AttachmentType, nextAttachment: AttachmentType) => boolean |
Not used. The component reads it from MessagesContext. |
|
message |
LocalMessage |
Not used. The component reads it from MessageContext. |
|
myMessageTheme |
DeepPartial<Theme> |
Not used. The component reads it from MessagesContext. |
|
urlPreviewType |
"compact" | "full" |
Not used. The component reads it from MessagesContext. |
AttachmentType is the Attachment type of stream-chat, imported under another name.
Gallery
Renders images and videos as a grid of up to four thumbnails, with "+3" over the last one when there are more. Pressing a thumbnail reports the gallery emitter with the thumbnail as additionalInfo; by default it opens the image gallery at that image or video. A prop set here wins over its context value.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for each thumbnail's Pressable, applied after its handlers. |
alignment |
Alignment |
from MessageContext |
Aligns the grid left or right. |
channelId |
string |
Not used. | |
imageGalleryStateStore |
ImageGalleryStateStore |
Not used. The component reads it from ImageGalleryContext. |
|
images |
Attachment[] |
from MessageContext |
The images to show. |
message |
LocalMessage |
from MessageContext |
The message the image gallery opens with. |
messageContentOrder |
MessageContentType[] |
from MessageContext |
With the gallery as the only part and one image or video, the thumbnail's bottom corners are square, so the bubble's corners show. |
messageHasOnlyOneMedia |
boolean |
Not used. Gallery works it out from messageContentOrder. |
|
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
The grid re-renders when it changes. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press on a thumbnail. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press on a thumbnail. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press on a thumbnail starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
setOverlay |
React.Dispatch<React.SetStateAction<Overlay>> |
from OverlayContext |
Shows the image gallery overlay. |
videos |
Attachment[] |
from MessageContext |
The videos to show. |
The sizes of the grid come from theme.messageItemView.gallery.
VideoThumbnail
Renders a video's thumbnail with a play icon, inside a Gallery. Without thumb_url it renders only its container.
| Prop | Type | Default | Description |
|---|---|---|---|
imageStyle |
StyleProp<ImageStyle> |
Style of the image. | |
localId |
string |
The ID of a pending upload, whose progress the thumbnail shows. | |
style |
StyleProp<ViewStyle> |
Style of the container. | |
thumb_url |
string |
The URL of the thumbnail image. |
Giphy
Renders a Giphy or Imgur attachment. A Giphy preview from the /giphy command, which has actions, shows "Only visible to you" and the action buttons, such as Send, Shuffle and Cancel; pressing one calls handleAction. A sent Giphy renders the image, and pressing it reports the giphy emitter. It renders nothing without image_url or thumb_url. A prop set here wins over its context value.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for the image's Pressable. |
attachment |
Attachment |
required | The Giphy or Imgur attachment. |
giphyVersion |
keyof Images |
from MessagesContext |
The Giphy rendition to show. |
handleAction |
ActionHandler |
from MessageContext |
Sends the action of a pressed button. |
isMyMessage |
boolean |
from MessageContext |
Picks the background color. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press on the image. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press on the image. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press on the image starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses on the image. |
GiphyImage renders the image itself, and is exported for a Giphy component of your own.
FileAttachmentGroup
Renders the message's files, each through the Attachment override, in a column.
| Prop | Type | Default | Description |
|---|---|---|---|
files |
Attachment[] |
from MessageContext |
The attachments to render. |
message |
LocalMessage |
Not used. The component reads it from MessageContext. |
|
styles |
Partial<{ attachmentContainer: StyleProp<ViewStyle>; container: StyleProp<ViewStyle>; }> |
Not used, since the component doesn't pass it on. Style the group through theme.messageItemView.fileAttachmentGroup. |
FileAttachment
Renders a file through the FilePreview override, with the file's icon and title, and its upload progress while it uploads. Pressing it reports the fileAttachment emitter with the attachment as additionalInfo; by default it opens the file's asset_url. A prop set here wins over its context value.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for the Pressable, applied after its handlers. |
attachment |
Attachment |
required | The file. |
attachmentIconSize |
"sm" | "md" | "lg" |
'md' |
The size of the file's icon. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
styles |
Partial<{ container: StyleProp<ViewStyle>; details: StyleProp<ViewStyle>; size: StyleProp<TextStyle>; title: StyleProp<TextStyle>; }> |
the component's own styles | Styles passed to FilePreview. A value replaces the defaults as a whole. |
FilePreview
Renders the FileAttachmentIcon override for the file's custom.mime_type, the title, and under it indicator, or the size or the duration when there's no indicator. It isn't exported; replace it with the FilePreview key.
| Prop | Type | Default | Description |
|---|---|---|---|
attachment |
Attachment |
required | The file. |
attachmentIconSize |
"sm" | "md" | "lg" |
'md' |
The size of the icon. |
indicator |
React.ReactNode |
Rendered in place of the size or duration line. FileAttachment passes its upload progress here. |
|
styles |
Partial<{ container: StyleProp<ViewStyle>; details: StyleProp<ViewStyle>; size: StyleProp<TextStyle>; title: StyleProp<TextStyle>; }> |
Styles merged after the theme's. | |
titleNumberOfLines |
number |
2 |
The number of lines of the title. |
FileIcon
Renders the icon for a MIME type, from the icons override map: PDF, DOC, SpreadSheet, Presentation, ZIP, Audio, Video, Code, or OtherFileIcon for any other type. It's the default of the FileAttachmentIcon key.
| Prop | Type | Default | Description |
|---|---|---|---|
mimeType |
string |
The file's MIME type. | |
size |
"sm" | "md" | "lg" |
'md' |
'sm' is 19 by 24 points, 'md' 32 by 40, 'lg' 40 by 48. |
AudioAttachment
Plays an audio attachment or a voice recording: a play button, the remaining time, a waveform for a voice recording with waveform_data or a progress bar otherwise, which the user can drag to seek, and a speed button. Each message's attachment has its own player; Voice recordings and audio playback explains how players are shared and when they stop. In a message, Attachment renders it with showTitle={false} and showSpeedSettings.
| Prop | Type | Default | Description |
|---|---|---|---|
containerStyle |
StyleProp<ViewStyle> |
Style of the container. | |
hideProgressBar |
boolean |
false |
Hides the waveform or progress bar. |
indicator |
React.ReactNode |
Rendered in place of the time and progress, and hides the speed button. Attachment passes the upload progress while the file uploads. |
|
isPreview |
boolean |
false |
Set when the attachment shows in the composer. The player is released when the preview unmounts. |
item |
AudioAttachmentType |
required | The attachment, with an id and type: 'audio' | 'voiceRecording'. |
message |
LocalMessage |
The message, which identifies the attachment's player. | |
showSpeedSettings |
boolean |
false |
Shows the playback speed button. |
showTitle |
boolean |
true |
Shows the title, or "Voice Message" for a voice recording. |
styles |
{ container?: StyleProp<ViewStyle>; playPauseButton?: StyleProp<ViewStyle>; speedSettingsButton?: StyleProp<ViewStyle>; durationText?: StyleProp<TextStyle>; leftContainer?: StyleProp<TextStyle>; } |
Styles of the parts. | |
testID |
string |
The container's testID. |
URLPreview and URLPreviewCompact
URLPreview, the default of the UrlPreview key, renders a link preview as a card: the image, with a play icon for a video link, the title, the description and the URL. URLPreviewCompact renders the same in a smaller layout, and urlPreviewType="compact" on Channel picks it. Pressing either reports the urlPreview emitter with the URL as additionalInfo; by default it opens og_scrape_url. Both remount when the message updates. Link previews covers the feature.
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for the card's Pressable, applied after its handlers. |
attachment |
Attachment |
required | The link preview. |
channelId |
string |
Not used. | |
ImageComponent |
React.ComponentType<ImageProps> |
the ImageComponent override |
Renders the image. |
isAttachmentEqual |
(prevAttachment: Attachment, nextAttachment: Attachment) => boolean |
from MessagesContext |
When set, decides whether the card re-renders once the SDK's own check finds the attachment unchanged. |
messageId |
string |
Not used. | |
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
The card re-renders when it changes. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
styles |
Partial<{ cardCover: StyleProp<ImageStyle>; cardFooter: StyleProp<ViewStyle>; container: StyleProp<ViewStyle>; description: StyleProp<TextStyle>; linkPreview: StyleProp<ViewStyle>; linkPreviewText: StyleProp<TextStyle>; title: StyleProp<TextStyle>; }> |
Styles of the parts. |
URLPreviewCompact takes the same props without isAttachmentEqual:
| Prop | Type | Default | Description |
|---|---|---|---|
additionalPressableProps |
Omit<PressableProps, "style"> |
from MessagesContext |
Props for the card's Pressable, applied after its handlers. |
attachment |
Attachment |
required | The link preview. |
channelId |
string |
Not used. | |
ImageComponent |
React.ComponentType<ImageProps> |
the ImageComponent override |
Renders the image. |
messageId |
string |
Not used. | |
myMessageTheme |
DeepPartial<Theme> |
from MessagesContext |
The card re-renders when it changes. |
onLongPress |
(payload: PressableHandlerPayload) => void |
from MessageContext |
Called on a long press. |
onPress |
(payload: MessagePressableHandlerPayload) => void |
from MessageContext |
Called on a press. |
onPressIn |
((payload: PressableHandlerPayload) => void) | null |
from MessageContext |
Called when a press starts. |
preventPress |
boolean |
from MessageContext |
Turns off presses. |
styles |
Partial<{ cardCover: StyleProp<ImageStyle>; cardFooter: StyleProp<ViewStyle>; container: StyleProp<ViewStyle>; description: StyleProp<TextStyle>; linkPreview: StyleProp<ViewStyle>; linkPreviewText: StyleProp<TextStyle>; title: StyleProp<TextStyle>; }> |
Styles of the parts. |
UnsupportedAttachment
Renders an attachment of a type the SDK doesn't know: the FileAttachmentIcon override for its custom.mime_type, and its title, or "Unsupported Attachment". It isn't exported; replace it with the UnsupportedAttachment key, or render your type from an Attachment override as above.
| Prop | Type | Default | Description |
|---|---|---|---|
attachment |
Attachment |
required | The attachment. |
attachmentIconSize |
"sm" | "md" | "lg" |
'md' |
The size of the icon. |
isMyMessage |
boolean |
Not used. The component reads it from MessageContext. |
Related hooks
useMessageContext()returns the message's attachments by kind:images,videos,filesandotherAttachments.useAudioPlayer()returns the player of an audio attachment, for a player of your own. See Voice recordings and audio playback.buildGallery()computes the gridGallerylays out.