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

Attachment picker

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 explains how picked files are uploaded, and Message composer UI 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 explains the insets.
  • Install the native modules for the sources you want. A source whose module is missing has no button. See 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:

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.

UI customization

Override key Replaces Props
AttachmentPickerSelectionBar The row of source buttons none
AttachmentPickerContent The content below the bar height
ImageOverlaySelectedComponent The mark on each cell of the photo grid: a ring, or the cell's number among the attachments index

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.
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.

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:

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 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 and useAttachmentPickerState.