# Attachment picker

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

`AttachmentPicker` is the bottom sheet the composer's attach button opens. `Channel` and `Thread` render it after their children, so every channel and thread screen has one. A selection bar at its top has a button for each source, photos, the camera, files, polls and commands, and the content below it shows the selected source. [Attachments and uploads](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/attachments-and-uploads/) explains how picked files are uploaded, and [Message composer UI](https://getstream.io/chat/docs/sdk/react-native/v10/ui/components/message-composer-ui/#the-attachment-picker) describes the picker in the composer.

## Best practices

- Configure the picker with the props of `Channel` and `Thread`. It has no props of its own, and the two screens don't share their settings.
- Pass the native header's height as `topInset`, so the sheet stops below the header. [Keyboard and insets](https://getstream.io/chat/docs/sdk/react-native/v10/ui/layout/keyboard-and-insets/) explains the insets.
- Install the native modules for the sources you want. A source whose module is missing has no button. See [Installation](https://getstream.io/chat/docs/sdk/react-native/v10/getting-started/installation/).
- Change the buttons by replacing `AttachmentPickerSelectionBar`, and the content by replacing `AttachmentPickerContent`. Render the exported buttons and contents in your replacement to keep the ones you want.
- Open and close the picker with `openAttachmentPicker()` and `closeAttachmentPicker()` from `useMessageInputContext()`. Opening that way also closes the keyboard, and does nothing when the picker isn't rendered.

## Basic usage

This channel screen sits below a native header and shows four photos per row:

```tsx title="src/ChannelScreen.tsx"
import { useHeaderHeight } from "@react-navigation/elements";
import type { Channel as StreamChannel } from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
} from "stream-chat-react-native";

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => {
  const headerHeight = useHeaderHeight();

  return (
    <Channel
      channel={channel}
      keyboardVerticalOffset={headerHeight}
      numberOfAttachmentPickerImageColumns={4}
      topInset={headerHeight}
    >
      <MessageList />
      <MessageComposer />
    </Channel>
  );
};
```

## What it renders

The sheet opens to `attachmentPickerBottomSheetHeight` and renders:

1. `AttachmentPickerSelectionBar`, the row of source buttons.
2. `AttachmentPickerContent`, the selected source, below the bar. With `disableAttachmentPicker`, the sheet renders the bar only, and each button opens the system picker directly.

Opening the picker closes the keyboard and selects the photo grid. Opening the keyboard closes it, and so does the Android back button. While it's open, the composer moves up above it. When `shouldRenderAttachmentPicker` is `false`, `Channel` and `Thread` don't render it, and `openAttachmentPicker()` does nothing; the attach button still shows, so hide it or give it a `handleAttachButtonPress`.

The default selection bar renders these buttons, each exported:

| Button                 | Shows when                                                       | With the sheet                                                                 | With `disableAttachmentPicker`                  |
| ---------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------- |
| `MediaPickerButton`    | `hasImagePicker`                                                 | selects the photo grid, `"images"`                                             | opens the native image picker                   |
| `CameraPickerButton`   | `hasCameraPicker`                                                | selects `"camera-photo"`; on Android, a second button selects `"camera-video"` | opens the camera                                |
| `FilePickerButton`     | `hasFilePicker`                                                  | selects `"files"` and opens the system file picker                             | opens the system file picker                    |
| `PollPickerButton`     | outside a thread, `hasCreatePoll` and the `send-poll` capability | selects `"polls"` and opens the poll creation dialog                           | opens the poll creation dialog                  |
| `CommandsPickerButton` | `hasCommands`                                                    | selects `"commands"`                                                           | opens the commands in a bottom sheet of its own |

`AttachmentPickerContent` renders, by the selected source:

| Source                             | Content                   | What it shows                                                                                                                                                                  |
| ---------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `"images"`                         | `AttachmentMediaPicker`   | A grid of the device's photos and videos, loaded `numberOfAttachmentImagesToLoadPerCall` at a time. Tapping one uploads it into the composer, and tapping it again removes it. |
| `"camera-photo"`, `"camera-video"` | `AttachmentCameraPicker`  | Opens the camera when it mounts, and a button that opens it again. When access was denied, a button that opens the settings.                                                   |
| `"files"`                          | `AttachmentFilePicker`    | A button that opens the system file picker.                                                                                                                                    |
| `"polls"`                          | `AttachmentPollPicker`    | A button that opens the poll creation dialog.                                                                                                                                  |
| `"commands"`                       | `AttachmentCommandPicker` | The channel's commands. Tapping one makes it the active command and focuses the input.                                                                                         |

When the user limited the app to some photos on iOS, the grid starts with a tile that lets them pick more. A source name of your own renders nothing in the default content; see [Add a source of your own](#add-a-source-of-your-own).

## UI customization

| Override key                    | Replaces                                                                                    | Props                        |
| ------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------- |
| `AttachmentPickerSelectionBar`  | The row of source buttons                                                                   | none                         |
| `AttachmentPickerContent`       | The content below the bar                                                                   | [`height`](#component-props) |
| `ImageOverlaySelectedComponent` | The mark on each cell of the photo grid: a ring, or the cell's number among the attachments | [`index`](#component-props)  |

`WithComponents` also accepts `AttachmentPickerIOSSelectMorePhotos`, but no component reads it.

## Props on Channel and Thread

| Prop                                    | Type                                                                       | Default                                                 | Description                                                                                                                                            |
| --------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `topInset`                              | `number`                                                                   | `0`                                                     | The space above the chat that isn't part of its views, such as a native header. The sheet doesn't open above it.                                       |
| `bottomInset`                           | `number`                                                                   | `0`                                                     | The space the sheet keeps free at the bottom. While the picker is open, the composer moves up by the sheet's height minus it.                          |
| `attachmentPickerBottomSheetHeight`     | `number`                                                                   | `333`, or `72` with `disableAttachmentPicker`           | The height of the open sheet.                                                                                                                          |
| `disableAttachmentPicker`               | `boolean`                                                                  | `true` when no media library module is installed        | Renders the selection bar only. Its buttons open the system pickers directly.                                                                          |
| `shouldRenderAttachmentPicker`          | `boolean`                                                                  | `true`                                                  | Whether the picker renders at all.                                                                                                                     |
| `numberOfAttachmentPickerImageColumns`  | `number`                                                                   | `3`                                                     | The columns of the photo grid.                                                                                                                         |
| `numberOfAttachmentImagesToLoadPerCall` | `number`                                                                   | `25`                                                    | How many photos the grid loads at a time.                                                                                                              |
| `hasImagePicker`                        | `boolean`                                                                  | whether an image picker or a media library is installed | Shows the photos button.                                                                                                                               |
| `hasCameraPicker`                       | `boolean`                                                                  | whether an image picker is installed                    | Shows the camera button.                                                                                                                               |
| `hasFilePicker`                         | `boolean`                                                                  | whether a document picker is installed                  | Shows the files button.                                                                                                                                |
| `hasCommands`                           | `boolean`                                                                  | whether the channel type has commands                   | Shows the commands button.                                                                                                                             |
| `hasCreatePoll`                         | `boolean`                                                                  | whether polls are on for the channel                    | Shows the poll button. `true` can't turn polls on where the composer's `polls.enabled` is `false`.                                                     |
| `openPollCreationDialog`                | `({ sendMessage }: Pick<LocalMessageInputContext, 'sendMessage'>) => void` | opens the default dialog                                | Called by the poll button in place of the default dialog. See [Poll components](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/poll-components/). |
| `handleAttachButtonPress`               | `() => void`                                                               | opens or closes the picker                              | Replaces what the attach button does.                                                                                                                  |
| `focusInputOnPickerClose`               | `boolean`                                                                  | `true`                                                  | Whether pressing the attach button while the picker is open focuses the input, rather than only closing the picker.                                    |
| `compressImageQuality`                  | `number`                                                                   | none                                                    | The quality, from 0 to 1, images are compressed to before upload.                                                                                      |
| `attachmentSelectionBarHeight`          | `number`                                                                   | `72`                                                    | Not read by any component.                                                                                                                             |

The full list of `Channel` props is on [Channel](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel/channel/).

## Component props

`AttachmentPicker`, `AttachmentPickerSelectionBar`, the five buttons and `AttachmentCommandPicker` take no props. They read the picker's context and `MessageInputContext`.

`AttachmentPickerContent`, `AttachmentMediaPicker`, `AttachmentFilePicker` and `AttachmentPollPicker` take `AttachmentPickerContentProps`:

| Prop     | Type     | Default | Description                                                                              |
| -------- | -------- | ------- | ---------------------------------------------------------------------------------------- |
| `height` | `number` | none    | The height of the content. `AttachmentPicker` passes the sheet's height minus the bar's. |

`AttachmentCameraPicker` also takes `videoOnly?: boolean`, which opens the camera for a video. `AttachmentCommandPickerItem` takes `item: CommandSuggestion`, the command it shows.

`ImageOverlaySelectedComponent`:

| Prop    | Type     | Default  | Description                                                                                                                                        |
| ------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `index` | `number` | required | The cell's position among the composer's attachments, or `-1` when it isn't attached. The default shows a ring at `-1`, and `index + 1` otherwise. |

`AttachmentTypePickerButton` is the button of the selection bar, for a source of your own:

| Prop                    | Type                                        | Default  | Description                                                             |
| ----------------------- | ------------------------------------------- | -------- | ----------------------------------------------------------------------- |
| `Icon`                  | `ButtonProps['LeadingIcon']`                | required | The icon, rendered at 20 points.                                        |
| `onPress`               | `null \| ((event: PressableEvent) => void)` | none     | Called on press, unless the button is selected and the sheet is in use. |
| `selected`              | `boolean`                                   | none     | Highlights the button, unless `disableAttachmentPicker` is `true`.      |
| `accessibilityLabelKey` | `string`                                    | none     | The translation key of the button's accessibility label.                |
| `testID`                | `string`                                    | none     | The test ID of the button.                                              |

`AttachmentPickerGenericContent` is the content the camera, files and polls sources show, for a source of your own:

| Prop          | Type                             | Default  | Description                |
| ------------- | -------------------------------- | -------- | -------------------------- |
| `Icon`        | `React.ComponentType<IconProps>` | required | The icon above the text.   |
| `onPress`     | `() => void`                     | required | Called by the button.      |
| `buttonText`  | `string`                         | none     | The button's label.        |
| `description` | `string`                         | none     | The text above the button. |
| `height`      | `number`                         | none     | The height of the content. |

## AttachmentPickerContext

`Channel` and `Thread` provide `AttachmentPickerContext`, which `useAttachmentPickerContext()` reads. It throws outside both.

| Value                                   | Type                                   | Description                                                                                                                                |
| --------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `attachmentPickerStore`                 | `AttachmentPickerStore`                | The store of the selected source. `setSelectedPicker(name)` selects one, and `undefined` clears it. `useAttachmentPickerState()` reads it. |
| `openPicker`                            | `() => void`                           | Opens the sheet. The sheet selects the photo grid when it has opened.                                                                      |
| `closePicker`                           | `() => void`                           | Closes the sheet. The sheet clears the selected source when it has closed.                                                                 |
| `bottomSheetRef`                        | `React.RefObject<BottomSheet \| null>` | The ref of the sheet, a `@gorhom/bottom-sheet` `BottomSheet`.                                                                              |
| `topInset`                              | `number`                               | The `Channel` or `Thread` prop, `0` by default.                                                                                            |
| `bottomInset`                           | `number`                               | The `Channel` or `Thread` prop, `0` by default.                                                                                            |
| `attachmentPickerBottomSheetHeight`     | `number`                               | The `Channel` or `Thread` prop.                                                                                                            |
| `attachmentSelectionBarHeight`          | `number`                               | The `Channel` or `Thread` prop. No component reads it.                                                                                     |
| `disableAttachmentPicker`               | `boolean`                              | The `Channel` or `Thread` prop.                                                                                                            |
| `shouldRenderAttachmentPicker`          | `boolean`                              | The `Channel` or `Thread` prop.                                                                                                            |
| `numberOfAttachmentPickerImageColumns`  | `number`                               | The `Channel` or `Thread` prop.                                                                                                            |
| `numberOfAttachmentImagesToLoadPerCall` | `number`                               | The `Channel` or `Thread` prop.                                                                                                            |

`AttachmentPickerProvider` and `AttachmentPickerContext` are exported too. `AttachmentPickerProvider` takes `openPicker`, `closePicker`, `topInset` and `bottomInset` and creates the store; `Channel` and `Thread` render it for you.

## Examples

### Add a source of your own

A source is a name in `attachmentPickerStore`. This selection bar adds a button that selects `"gifs"`, and the content renders a component of your own for it and the SDK's content for the other sources:

```tsx title="src/pickerOverrides.tsx"
import { Text, View } from "react-native";
import type { ComponentOverrides } from "stream-chat-react-native";
import type { AttachmentPickerContentProps } from "stream-chat-react-native";
import {
  AttachmentPickerContent,
  AttachmentTypePickerButton,
  CameraPickerButton,
  FilePickerButton,
  MediaPickerButton,
  useAttachmentPickerContext,
  useAttachmentPickerState,
  useComponentsContext,
} from "stream-chat-react-native";

const GifPickerButton = () => {
  const { attachmentPickerStore } = useAttachmentPickerContext();
  const { selectedPicker } = useAttachmentPickerState();
  const { icons } = useComponentsContext();

  return (
    <AttachmentTypePickerButton
      Icon={icons.GiphyIcon}
      onPress={() => attachmentPickerStore.setSelectedPicker("gifs")}
      selected={selectedPicker === "gifs"}
    />
  );
};

const SelectionBarWithGifs = () => (
  <View style={{ flexDirection: "row", paddingHorizontal: 12 }}>
    <MediaPickerButton />
    <CameraPickerButton />
    <FilePickerButton />
    <GifPickerButton />
  </View>
);

const ContentWithGifs = (props: AttachmentPickerContentProps) => {
  const { selectedPicker } = useAttachmentPickerState();

  if (selectedPicker === "gifs") {
    return (
      <View style={{ height: props.height, padding: 16 }}>
        <Text>Your GIF search goes here.</Text>
      </View>
    );
  }

  return <AttachmentPickerContent {...props} />;
};

// Pass to `WithComponents`, declared once at module scope.
export const pickerOverrides: ComponentOverrides = {
  AttachmentPickerContent: ContentWithGifs,
  AttachmentPickerSelectionBar: SelectionBarWithGifs,
};
```

A button of your own isn't hidden by `disableAttachmentPicker`, which leaves the content out. Add files from your content with `uploadNewFile(file)` from `useMessageInputContext()`. The [Location sharing UI cookbook](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/app/location-sharing/) adds a location source this way.

## Hooks

| Hook                           | Returns                                                                                                   |
| ------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `useAttachmentPickerContext()` | the values above; throws outside `Channel` and `Thread`                                                   |
| `useAttachmentPickerState()`   | `{ selectedPicker }`, the selected source, or `undefined` while the picker is closed                      |
| `useMessageInputContext()`     | `openAttachmentPicker()`, `closeAttachmentPicker()`, the pickers' functions and the `has*Picker` settings |

See [useAttachmentPickerContext](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#useattachmentpickercontext) and [useAttachmentPickerState](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#useattachmentpickerstate).

---

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