import { SearchIndex } from "emoji-mart";
import {
Avatar,
Channel,
MessageInput,
SuggestionList,
type SuggestionListItemComponentProps,
type SuggestionListProps,
defaultComponents,
WithComponents,
} from "stream-chat-react";
const MentionItem = ({
entity,
focused,
...buttonProps
}: SuggestionListItemComponentProps) => {
const user = entity as {
id?: string;
image?: string;
name?: string;
};
const title = user.name ?? user.id;
if (!title) return null;
return (
<button
{...buttonProps}
className={`suggestion-list__item ${focused ? "suggestion-list__item--selected" : ""}`}
>
<Avatar imageUrl={user.image} size={32} userName={title} />
<span>{title}</span>
</button>
);
};
const CustomSuggestionList = (props: SuggestionListProps) => (
<SuggestionList
{...props}
suggestionItemComponents={{
...defaultComponents,
"@": MentionItem,
}}
/>
);
const App = () => (
<WithComponents
overrides={{
AutocompleteSuggestionList: CustomSuggestionList,
emojiSearchIndex: SearchIndex,
}}
>
<Channel>
<MessageInput />
</Channel>
</WithComponents>
);Autocomplete Suggestions
Message input supports autocompletion for mentions, commands, and emojis.
Autocomplete suggestions are triggered by typing:
| Trigger | Action | Example |
|---|---|---|
@ | mention | @tom |
/ | command | /giphy |
: | emoji | :smiling |
Best Practices
- Keep suggestion lists keyboard accessible and preserve the default focus behavior.
- Use
SuggestionListanddefaultComponentsas the starting point for custom list UI. - Override the list through
WithComponentsso every nested composer stays consistent. - Keep custom suggestion item rendering lightweight.
- Prefer using
TextareaComposerinside custom inputs instead of rebuilding autocomplete behavior from scratch.
Customizing Suggestion Items
The default list implementation is SuggestionList. The simplest way to customize item rendering is to wrap it and replace one or more suggestionItemComponents.
.suggestion-list__item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 12px;
border: 0;
background: none;
font: inherit;
}
.suggestion-list__item--selected {
background: #00000014;
}Wrapping The Default Suggestion List
If you only need extra framing around the list, keep the default list behavior and wrap SuggestionList:
import { SuggestionList, type SuggestionListProps } from "stream-chat-react";
const SuggestionListWithHeader = (props: SuggestionListProps) => (
<div className="suggestion-list-shell">
<div className="suggestion-list-shell__header">Suggestions</div>
<SuggestionList {...props} />
</div>
);Register it with WithComponents the same way:
<WithComponents
overrides={{ AutocompleteSuggestionList: SuggestionListWithHeader }}
>
<Channel>
<MessageInput />
</Channel>
</WithComponents>Replacing AutocompleteSuggestionItem
If you need to replace the selection wrapper itself, override AutocompleteSuggestionItem.
The current item wrapper receives SuggestionItemProps, which include:
itemfocusedcomponent- the button props passed through to the rendered item
import {
SuggestionListItem,
type SuggestionItemProps,
} from "stream-chat-react";
const CustomAutocompleteSuggestionItem = (props: SuggestionItemProps) => (
<SuggestionListItem
{...props}
className="custom-autocomplete-suggestion-item"
/>
);For most apps, overriding AutocompleteSuggestionList and using suggestionItemComponents is enough. Reach for AutocompleteSuggestionItem only when you want to change the shared keyboard and selection wrapper.
Custom Inputs
If you build a custom input layout, keep TextareaComposer in the tree and override the suggestion list through WithComponents. That preserves the SDK text composer, keyboard navigation, and suggestion positioning logic.
For a full custom input example, see the Input UI cookbook.