Custom Autocomplete Suggestion List

Autocomplete suggestion list components can be customized via WithComponents:

  • AutoCompleteSuggestionHeader
  • AutoCompleteSuggestionItem
  • AutoCompleteSuggestionList

Best Practices

  • Preserve default components for triggers you don't customize to keep expected behavior.
  • Keep list rendering lightweight; suggestion updates happen on every keystroke.
  • Use keyboardShouldPersistTaps="always" in custom lists to avoid dismissing the keyboard.
  • Normalize triggerType mapping (/, :, @) to avoid mismatched UI.
  • Ensure onSelect updates the composer state rather than mutating input directly.

Header and item components are used inside the list's FlatList. These props let you customize the header, items, or the entire list.

The available suggestion trigger types are:

  • command for /
  • emoji for :
  • mention for @

You can render custom UI for specific triggers and fall back to defaults for the rest.

Customizing Header component

The header renders at the top of the list and is customized via AutoCompleteSuggestionHeader.

The props for the component are:

  • triggerType: The trigger type of the suggestion list.
  • queryText: The value to display in the header.

An example for the same would be as follows:

import {
  AutoCompleteSuggestionHeader,
  WithComponents,
} from "stream-chat-react-native";
import { Text } from "react-native";

const CustomAutoCompleteSuggestionHeader = ({ queryText, triggerType }) => {
  if (triggerType === "command") {
    return <Text>Command Header Component</Text>;
  } else if (triggerType === "emoji") {
    return <Text>Emoji Header Component</Text>;
  } else {
    return (
      <AutoCompleteSuggestionHeader
        queryText={queryText}
        triggerType={triggerType}
      />
    );
  }
};

<WithComponents
  overrides={{
    AutoCompleteSuggestionHeader: CustomAutoCompleteSuggestionHeader,
  }}
>
  <Channel channel={channel}>{/* The underlying components */}</Channel>
</WithComponents>;

Note: Return the default AutoCompleteSuggestionHeader to keep default behavior for a trigger.

Customizing Item Component

Customize list items via the AutoCompleteSuggestionItem override in WithComponents.

The props for the component are as follows:

  • triggerType: The trigger type of the suggestion list.
  • itemProps: Varies by trigger. command has name and args, emoji is an Emoji, and mention is SuggestionUser<Us>.
import {
  Avatar,
  AutoCompleteSuggestionItem,
  WithComponents,
} from "stream-chat-react-native";
import { Text, View } from "react-native";

const CustomAutoCompleteSuggestionItem = ({ itemProps, triggerType }) => {
  if (triggerType === "command") {
    return (
      <View>
        <Text>{itemProps.name}</Text>
        <Text>{itemProps.args}</Text>
      </View>
    );
  } else if (triggerType === "mention") {
    const { id, image, name, online } = itemProps;
    return (
      <View>
        <Avatar image={image} name={name} online={online} size={30} />
        <Text>{itemProps.name}</Text>
      </View>
    );
  } else {
    return (
      <AutoCompleteSuggestionItem
        itemProps={itemProps}
        triggerType={triggerType}
      />
    );
  }
};

<WithComponents
  overrides={{ AutoCompleteSuggestionItem: CustomAutoCompleteSuggestionItem }}
>
  <Channel channel={channel}>{/*The underlying components*/}</Channel>
</WithComponents>;

Note: Return the default AutoCompleteSuggestionItem to keep default behavior for a trigger.

Customizing the AutoComplete suggestion list

You can also replace the entire list via the AutoCompleteSuggestionList override in WithComponents.

The props available to the component are:

  • AutoCompleteSuggestionHeader - The header component to be used in the list.
  • AutoCompleteSuggestionItem - The item component to be used in the list.

Example:

import {
  AutoCompleteSuggestionHeader,
  AutoCompleteSuggestionItem,
  useMessageComposer,
  useStateStore,
  WithComponents,
} from "stream-chat-react-native";
import { FlatList, Text, View } from "react-native";
import { TextComposerState, SearchSourceState } from "stream-chat";

const textComposerStateSelector = (state: TextComposerState) => ({
  suggestions: state.suggestions,
  text: state.text,
});

const searchSourceStateSelector = (nextValue: SearchSourceState) => ({
  items: nextValue.items,
});

const CustomAutoCompleteSuggestionList = () => {
  const messageComposer = useMessageComposer();
  const { textComposer } = messageComposer;
  const { suggestions } = useStateStore(
    textComposer.state,
    textComposerStateSelector,
  );
  const { items } =
    useStateStore(suggestions?.searchSource.state, searchSourceStateSelector) ??
    {};
  const trigger = suggestions?.trigger;
  const queryText = suggestions?.query;

  const triggerType = {
    "/": "command",
    ":": "emoji",
    "@": "mention",
  }[trigger ?? ""];

  const onSelect = (item) => {
    // Handle the selection of the item
    console.log("Selected item:", item);
  };

  if (triggerType === "command") {
    return (
      <View>
        <View>
          <Text>Command suggestions for {queryText}</Text>
        </View>
        {data.map((item) => (
          <AutoCompleteSuggestionItem
            itemProps={item}
            key={item.name}
            triggerType={triggerType}
          />
        ))}
      </View>
    );
  } else if (triggerType === "emoji") {
    return (
      <FlatList
        data={data}
        keyboardShouldPersistTaps="always"
        ListHeaderComponent={
          <AutoCompleteSuggestionHeader
            queryText={queryText}
            triggerType={triggerType}
          />
        }
        renderItem={({ index, item }) => (
          <TouchableOpacity
            onPress={() => {
              onSelect(item);
            }}
          >
            <Text>{item.unicode}</Text>
          </TouchableOpacity>
        )}
      />
    );
  } else {
    return (
      <View>
        <AutoCompleteSuggestionHeader
          queryText={queryText}
          triggerType={triggerType}
        />
        {data.map((item) => (
          <AutoCompleteSuggestionItem
            itemProps={item}
            key={item.name}
            triggerType={triggerType}
          />
        ))}
      </View>
    );
  }
};

<WithComponents
  overrides={{ AutoCompleteSuggestionList: CustomAutoCompleteSuggestionList }}
>
  <Channel channel={channel}>{/*The underlying components*/}</Channel>
</WithComponents>;

Note: Return the default AutoCompleteSuggestionList to keep default behavior for a trigger.