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

MessageInputContext

MessageInputContext holds the composer settings set on Channel or Thread, and the functions the composer UI calls: send, the pickers, the attachment picker and the voice recorder. Channel and Thread provide it to everything they render, and every part of the composer UI reads it with useMessageInputContext(). It holds UI settings and actions, not the message: the text, the attachments and the rest are in the composer's stores, which Message composer UI maps to the parts that read them.

Best practices

  • Read the message being written from the composer, with useMessageComposer() and useStateStore, not from this context.
  • Send with sendMessage() from this context, so your controls send, edit and check links the way the default send button does.
  • Set the values on Channel and Thread. The context is memoized and gets a new value only when hasCommands changes or the poll creation dialog opens or closes, so a prop changed after mount, such as audioRecordingEnabled, doesn't reach it. Remount the Channel or Thread, for example with a key, to apply it.
  • Read the cooldown with useCooldownRemaining. It isn't in this context.
  • Render components that read it inside a Channel or a Thread. useMessageInputContext() throws outside both.

Basic usage

This button sits next to the composer and opens the attachment picker on the photo grid:

src/AddPhotosButton.tsx
import { Pressable, Text } from "react-native";
import { useMessageInputContext } from "stream-chat-react-native";

export const AddPhotosButton = () => {
  const { hasImagePicker, openAttachmentPicker } = useMessageInputContext();

  if (!hasImagePicker) return null;

  return (
    <Pressable onPress={openAttachmentPicker}>
      <Text>Add photos</Text>
    </Pressable>
  );
};

openAttachmentPicker() closes the keyboard, selects the photo grid and opens the sheet. It does nothing when shouldRenderAttachmentPicker is false on Channel or Thread.

Values

Each Channel and Thread creates its own audioRecorderManager, inputBoxRef and messageInputHeightStore, so a channel screen and a thread screen don't share a recorder or an input.

Value Type Description
sendMessage () => Promise<void> Sends the composition with MessageComposer.send(), or saves the edit with MessageComposer.update() while a message is being edited. Saving the edit of a bounced message sends it again as a new message. It clears the input first, and puts the text back when there was nothing to send. When the text has a link and the user lacks send-links, it shows an alert and sends nothing.
inputBoxRef React.RefObject<InputBoxRef | null> The text input. Besides the TextInput methods, such as focus(), it has clearState() and restoreState(text), which set the input's text without changing the composer.
setInputBoxRef Ref<InputBoxRef> | undefined The ref callback the text input uses. It fills inputBoxRef and calls setInputRef.
setInputRef (ref: TextInputRef | null) => void The Channel or Thread prop. Receives the text input, and null when it unmounts.
additionalTextInputProps TextInputProps The Channel or Thread prop. MessageComposer spreads it onto the TextInput.
messageInputFloating boolean The Channel or Thread prop, false by default. The composer floats over the message list, and the list leaves room for it.
messageInputHeightStore MessageInputHeightStore A store whose store holds the composer's measured height. The message list reads it to leave room for a floating composer.
openAttachmentPicker () => void Closes the keyboard, selects the photo grid and opens the attachment picker. Does nothing when shouldRenderAttachmentPicker is false.
closeAttachmentPicker () => void Clears the selected source and closes the attachment picker.
focusInputOnPickerClose boolean The Channel or Thread prop, true by default. When the user presses the attach button while the picker is open, true focuses the input, which opens the keyboard and closes the picker, and false closes the picker without focusing.
handleAttachButtonPress () => void The Channel or Thread prop. Replaces what the attach button does, which is to open or close the picker.
attachmentPickerBottomSheetHeight number The Channel or Thread prop: the height of the picker when open. 333 by default, or 72 when disableAttachmentPicker is true.
attachmentSelectionBarHeight number The Channel or Thread prop, 72 by default. No component reads it.
hasImagePicker boolean The Channel or Thread prop. Whether the picker shows the photos button. By default, whether an image picker or a media library is installed.
hasCameraPicker boolean The Channel or Thread prop. Whether the picker shows the camera button. By default, whether an image picker is installed.
hasFilePicker boolean The Channel or Thread prop. Whether the picker shows the files button. By default, whether a document picker is installed.
hasCommands boolean The Channel or Thread prop. Whether the picker shows the commands button. By default, whether the channel type has commands.
pickAndUploadImageFromNativePicker () => Promise<void> Opens the native image picker for as many images as the message has room for, and uploads the picked ones into the composer. Asks the user to open the settings when access was denied.
takeAndUploadImage (mediaType?: MediaTypes) => Promise<{ askToOpenSettings?: boolean; canceled?: boolean } | undefined> Opens the camera for a photo, a video or both ("image", "video" or "mixed"), and uploads the result into the composer. Resolves to the camera's result, with askToOpenSettings when access was denied and cancelled when the user closed the camera (the type spells it canceled), or to undefined when the message has no room for another file.
pickFile () => Promise<void> Opens the system file picker and uploads the picked files into the composer. Does nothing without a document picker.
uploadNewFile (file: File) => Promise<void> Uploads a file into the composer through its attachment manager. It sets the file's type from its name when missing, and compresses images with compressImageQuality first. A file the upload rules block is removed from the composer.
compressImageQuality number The Channel or Thread prop, from 0 to 1. Without it, picked images upload as they are, and photos the camera takes use the camera handler's own quality.
audioRecordingEnabled boolean The Channel or Thread prop, false by default. Whether the record button shows while the composer is empty.
audioRecordingSendOnComplete boolean The Channel or Thread prop, false by default. Whether a finished recording is sent at once, instead of waiting in the composer.
asyncMessagesMinimumPressDuration number The Channel or Thread prop, 500 by default: how long, in milliseconds, the user holds the record button to start recording.
asyncMessagesSlideToCancelDistance number The Channel or Thread prop, 75 by default: how far, in points, the user slides to the left to cancel a recording. Releasing past half of it cancels.
asyncMessagesLockDistance number The Channel or Thread prop, 50 by default: how far, in points, the user slides up to lock a recording. Releasing past half of it locks.
audioRecorderManager AudioRecorderManager The recorder. Its state store holds status ("idle", "recording" or "stopped"), duration in milliseconds, waveformData, micLocked, isStarting and the recording.
startVoiceRecording () => Promise<boolean | undefined> Starts recording. Resolves to whether the microphone permission was granted.
stopVoiceRecording () => Promise<void> Stops recording and keeps the recording, for the preview.
deleteVoiceRecording () => Promise<void> Stops recording and throws the recording away.
uploadVoiceRecording (sendOnComplete: boolean) => Promise<void> Stops recording and turns the recording into a voiceRecording attachment. With true, it adds the attachment and sends the message at once; with false, it uploads the attachment into the composer.
openPollCreationDialog ({ sendMessage }: Pick<LocalMessageInputContext, 'sendMessage'>) => void Called by the poll button of the picker. It calls the Channel or Thread prop of the same name with { sendMessage } when you set one, and opens the default poll creation dialog otherwise.
showPollCreationDialog boolean Whether the default poll creation dialog is open.
closePollCreationDialog () => void Closes the default poll creation dialog.
createPollOptionGap number The Channel or Thread prop. The default MessageComposer doesn't pass it to the poll form, which uses 8 unless MessageComposer gets a createPollOptionGap prop of its own.

MessageInputContextValue is the type of the value, and MessageInputContext itself is exported, so useContext(MessageInputContext) returns the same as useMessageInputContext() without the check for a provider. InputBoxRef, TextInputRef, File and LocalMessageInputContext are exported types.

MessageComposerAPIContext

Channel and Thread also provide MessageComposerAPIContext, with the functions that start and end an edit and set the quoted message. useMessageComposerAPIContext() reads it, and throws outside both.

Value Type Description
setEditingState (message?: LocalMessage) => void Starts editing message: it takes the message's composer from client.messageComposerCache, or creates one, and loads the message into it. useMessageComposer() returns that composer until the edit ends. Without a message, it ends the edit.
clearEditingState () => void Ends the edit without saving it.
setQuotedMessage MessageComposer['setQuotedMessage'] Sets the quoted message of the composer useMessageComposer() returns, or clears it with null.

An edit also ends when the edit composer's editedMessage is cleared, which saving the edit does. Sending, editing and deleting shows the edit flow.

Examples

Insert text and focus the input

A part that writes into the composer gets the composer from useMessageComposer() and the input from inputBoxRef. This leading view keeps the attach button and adds a greeting button:

src/LeadingViewWithGreeting.tsx
import { Pressable, Text, View } from "react-native";
import {
  MessageComposerLeadingView,
  useMessageComposer,
  useMessageInputContext,
} from "stream-chat-react-native";

export const LeadingViewWithGreeting = () => {
  const { inputBoxRef } = useMessageInputContext();
  const { textComposer } = useMessageComposer();

  const greet = () => {
    textComposer.insertText({ text: "Hi there! " });
    inputBoxRef.current?.focus();
  };

  return (
    <View style={{ alignItems: "center", flexDirection: "row" }}>
      <MessageComposerLeadingView />
      <Pressable onPress={greet}>
        <Text>Greet</Text>
      </Pressable>
    </View>
  );
};

Register it as MessageComposerLeadingView with WithComponents. It renders the default view, so the attach button stays.

Send from your own button

sendMessage() sends what the composer holds, or saves the edit. Pair it with useMessageComposerHasSendableData(), which the default send button uses to enable itself:

src/TextSendButton.tsx
import { Pressable, Text } from "react-native";
import {
  useMessageComposerHasSendableData,
  useMessageInputContext,
} from "stream-chat-react-native";

export const TextSendButton = () => {
  const { sendMessage } = useMessageInputContext();
  const hasSendableData = useMessageComposerHasSendableData();

  return (
    <Pressable disabled={!hasSendableData} onPress={sendMessage}>
      <Text>Send</Text>
    </Pressable>
  );
};

Register it as SendButton. The default trailing view still shows the record button, the cooldown timer and the edit button in their cases, and your button otherwise.

Hooks