# Attachments

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

`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](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/attachments-and-uploads/#in-the-message-list) 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](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/attachments-and-uploads/#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:

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

```tsx
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](https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/baseimage/) and [ComponentContext](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/componentcontext/).

## Image and video sizing

The CSS variable [`--str-chat__attachment-max-width`](https://getstream.io/chat/docs/sdk/react/v15/reference/theming-variables/component-variables/) sets the largest width and height of images and videos. It must resolve to pixels in [`getComputedStyle`](https://developer.mozilla.org/en-US/docs/Web/API/Window/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:

```tsx
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`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/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](https://getstream.io/chat/docs/sdk/react/v15/component-reference/voice-recording/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](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/attachmentcontext/).
- `useAttachmentUploadState(attachment)` returns `isUploading`, `progress` and `uploadConfirmationPending`, for a renderer that shows an upload in progress.
- `useMessageContext()` gives `handleAction` and the message.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/attachments/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/message/attachments/).