Skip to main content
Version: v11 (legacy)

Message Input UI

How-to Guide for Building a Custom Message Input

The React Chat component library provides a highly customizable MessageInput component. We'll outline the various ways in which you can customize the look and behavior of this component, ranging from simple style changes, all the way to creating a completely new input with custom logic.

Styling

The default Input UI component comes with predefined styles and CSS classes. The easiest way to customize styling is to write your own CSS and override the default values. To get a sense of all the classes and styles applied to the MessageInput and its child components, you can either inspect the DOM inside your browser.

For example, here's how you can override the styles for the underlying textarea and make its background light blue:

.str-chat .str-chat__textarea > textarea {
background-color: #99ccff;
}

Customize Functionality

The MessageInput component supports a variety of props that let you customize its behavior in various ways. You can utilize props to change basic behaviors of the underlying textarea element (ex: the grow prop lets you specify whether the input field should automatically increase in height when the message wraps the input), as well as override functions and more complex and logic-based parts of the component (ex: the doImageUploadRequest prop lets you supply a custom function that handles the uploading of image attachments).

note

For a complete overview of props, take a look at the MessageInput props section.

Override UI Components

You can override the UI components rendered inside the MessageInput via Channel props. These are then injected into the ComponentContext, and in turn are consumed by the MessageInput.

Here's an example of overriding the default EmojiIcon component:

const CustomEmojiIcon = () => {
const { t } = useTranslationContext();

return (
<div>
<img src='icon.svg' alt={t('Open emoji picker')} />
</div>
);
};

<Chat client={client}>
<Channel channel={channel} EmojiIcon={CustomEmojiIcon}>
<MessageList />
<MessageInput />
</Channel>
</Chat>;

Custom Triggers

The MessageInput component supports autocomplete triggers. When you type a special character (by default: @ for mentions, : for emoji, / for commands), a suggestion list pops up and auto-completes results based on the input text. The default behavior of these triggers can be extended and/or overridden. Meaning, you can add your own custom trigger, or modify the behavior of the default triggers.

The Channel component exposes a TriggerProvider prop, which defaults to the DefaultTriggerProvider component. This component injects the approved set of triggers into the autocompleteTriggers value of the MessageInputContext. This value is consumed by the ChatAutoComplete component and its children to ensure proper trigger functionality. By injecting a custom TriggerProvider component, you can adjust the behavior of any of these triggers.

Here's an example of a custom TriggerProvider that overrides the default values with the # character:

import React from 'react';
import { MessageInputContextProvider, useMessageInputContext } from 'stream-chat-react';

const options = ['some', 'thing', 'that', 'totally', 'works'];

const CustomSuggestionItem = (props) => <div>{props.entity.name}</div>;

const customTrigger = {
component: CustomSuggestionItem,
dataProvider: (query, _, onReady) => {
const filteredOptions = options
.filter((option) => option.includes(query))
.map((option) => ({ name: option }));
onReady(filteredOptions, query);
},
output: (entity) => ({
caretPosition: 'next',
key: entity.name,
text: entity.name,
}),
};

const customTriggers = {
'#': customTrigger,
};

export const CustomTriggerProvider = ({ children }) => {
const currentContextValue = useMessageInputContext();

const updatedContextValue = {
...currentContextValue,
autocompleteTriggers: customTriggers,
};

return (
<MessageInputContextProvider value={updatedContextValue}>
{children}
</MessageInputContextProvider>
);
};

The CustomTriggerProvider component is then added as a prop onto Channel to override the default trigger behavior.

<Chat client={client}>
<Channel channel={channel} TriggerProvider={CustomTriggerProvider}>
<MessageList />
<MessageInput />
</Channel>
</Chat>

Building the Input UI

The MessageInput component wraps and provides all the stateful logic needed to build your own Input UI component. Both Channel and MessageInput accept an Input prop, which lets you pass in your own UI component that consumes the MessageInputContext and handles the input's display.

note

If an Input prop is not provided, MessageInput renders MessageInputFlat by default.

We provide a useMessageInputContext custom hook, which lets you access all the stateful data and functionality needed to create your own custom Input UI component.

success

Use MessageInputFlat as a guide to help you build your own custom Input UI component.

The below example shows how to build a simple Input UI component, which calls the useMessageInputContext hook and use its return values to build functionality:

import {
ChatAutoComplete,
EmojiIconLarge,
EmojiPicker,
SendButton,
Tooltip,
useMessageInputContext,
useTranslationContext,
} from 'stream-chat-react';

export const CustomMessageInput = () => {
const { t } = useTranslationContext();

const {
closeEmojiPicker,
emojiPickerIsOpen,
handleEmojiKeyDown,
handleSubmit,
openEmojiPicker,
} = useMessageInputContext();

return (
<div className='str-chat__input-flat str-chat__input-flat--send-button-active'>
<div className='str-chat__input-flat-wrapper'>
<div className='str-chat__input-flat--textarea-wrapper'>
<ChatAutoComplete />
<div className='str-chat__emojiselect-wrapper'>
<Tooltip>
{emojiPickerIsOpen ? t('Close emoji picker') : t('Open emoji picker')}
</Tooltip>
<span
className='str-chat__input-flat-emojiselect'
onClick={emojiPickerIsOpen ? closeEmojiPicker : openEmojiPicker}
onKeyDown={handleEmojiKeyDown}
role='button'
tabIndex={0}
>
<EmojiIconLarge />
</span>
</div>
<EmojiPicker />
</div>
<SendButton sendMessage={handleSubmit} />
</div>
</div>
);
};

Once you've created your custom input component, you render it by adding the Input prop to either the Channel or MessageInput component. Adding onto Channel will store the component in the ComponentContext, whereas adding onto MessageInput will override any context value.

<Chat client={client}>
<Channel channel={channel} Input={CustomMessageInput}>
<MessageList />
<MessageInput />
</Channel>
</Chat>

Did you find this page helpful?