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

The `mention` trigger surfaces a typed `MentionSuggestion` union from `stream-chat` with five variants discriminated by `item.mentionType`:

| `mentionType` | Token      | Notifies                      |
| ------------- | ---------- | ----------------------------- |
| `user`        | `@<name>`  | A single user                 |
| `channel`     | `@channel` | Everyone in the channel       |
| `here`        | `@here`    | Members currently online      |
| `role`        | `@<role>`  | Users with that role          |
| `user_group`  | `@<group>` | Members of a named user group |

The default `AutoCompleteSuggestionItem` dispatches each variant to its own row component. See [Customizing per-mention-type rows](#customizing-per-mention-type-rows) to override one or more variants while keeping the SDK defaults for the rest.

### Enabling enhanced mentions on the dashboard

`@<user>` mentions work out of the box. The broadcast (`@channel`, `@here`), role (`@<role>`), and user-group (`@<group>`) variants require the matching permission to be granted to the sender's role on the channel type. Until the permissions are enabled, the variants are filtered out of the suggestion list returned by the API and the rendered tokens fall back to plain text.

Open the [Stream Dashboard](https://getstream.io/signin/?product=chat), pick your app, then navigate to **Chat Messaging → Roles & Permissions**. Select the role you want to grant the capability to (for example `channel_member`) and the scope of the channel type that should allow it (for example `messaging`), then enable the relevant permissions:

| Permission      | Enables    |
| --------------- | ---------- |
| `NotifyChannel` | `@channel` |
| `NotifyHere`    | `@here`    |
| `NotifyRole`    | `@<role>`  |
| `NotifyGroup`   | `@<group>` |

![Stream Dashboard — Roles & Permissions page with the NotifyChannel, NotifyHere, NotifyRole and NotifyGroup permissions enabled for the channel_member role in the messaging scope](https://getstream.io/docs-assets/images/e542c0d0d6b4.png)

User group mentions additionally require user groups to be defined on the app via the [user groups API](https://getstream.io/chat/docs/react-native/user-groups/). Role mentions resolve against the roles defined under **Chat Messaging → Roles & Permissions** in the same dashboard.

### 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:

```tsx
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 a `MentionSuggestion` union (from `stream-chat`) — switch on `itemProps.mentionType` to distinguish `user`, `channel`, `here`, `role`, and `user_group` variants.

```tsx
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 } = itemProps;
    return (
      <View>
        <Avatar imageUrl={image} name={name} size="md" />
        <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 per mention type rows

For the `mention` trigger, the SDK ships a built-in dispatcher (`MentionSuggestionItem`) that switches on `item.mentionType` and renders one of four built-in row components:

| `mentionType`      | Default component      | Renders                                             |
| ------------------ | ---------------------- | --------------------------------------------------- |
| `user`             | `MentionUserItem`      | Avatar + tokenized display name (matched part bold) |
| `channel` / `here` | `MentionBroadcastItem` | Megaphone icon + `@channel` / `@here` + description |
| `role`             | `MentionRoleItem`      | Shield icon + role name + "Notify all … members"    |
| `user_group`       | `MentionUserGroupItem` | People icon + group name + optional description     |

To swap **just one variant** while delegating the rest to the SDK defaults, override `AutoCompleteSuggestionItem` and pass any unmodified variants through. The variant components and their underlying building blocks are exported from `stream-chat-react-native`:

```tsx
import {
  AutoCompleteSuggestionItem,
  EnhancedMentionContent,
  EnhancedMentionIcon,
  MentionItem,
  WithComponents,
} from "stream-chat-react-native";

const MyBroadcastRow = ({ itemProps }) => (
  <MentionItem
    leading={
      <EnhancedMentionIcon
        Icon={({ height, width }) => (
          <Text style={{ height, width, textAlign: "center" }}>📣</Text>
        )}
      />
    }
  >
    <EnhancedMentionContent
      title={itemProps.mentionType === "channel" ? "@channel" : "@here"}
      subtitle={
        itemProps.mentionType === "channel"
          ? "Notify everyone in this channel"
          : "Notify every online member"
      }
    />
  </MentionItem>
);

const CustomAutoCompleteSuggestionItem = ({ itemProps, triggerType }) => {
  if (
    triggerType === "mention" &&
    (itemProps.mentionType === "channel" || itemProps.mentionType === "here")
  ) {
    return <MyBroadcastRow itemProps={itemProps} />;
  }

  return (
    <AutoCompleteSuggestionItem
      itemProps={itemProps}
      triggerType={triggerType}
    />
  );
};

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

Falling through to `AutoCompleteSuggestionItem` for everything else keeps the SDK defaults for the trigger types and mention variants you don't touch — you only need to handle the variants you actually want to override.

The building blocks you can compose with — all exported from `stream-chat-react-native`:

- `MentionItem` — the row layout primitive (`{ leading?, children, testID? }`). Use it as the outer wrapper to inherit the default tap target and spacing.
- `EnhancedMentionContent` — title + optional subtitle pair (`{ title, subtitle? }`).
- `EnhancedMentionIcon` — circular icon chip wrapping any `IconProps`-shaped component (`{ Icon, size?, color? }`).
- `TokenizedSuggestionParts` — renders a tokenized display name and bolds the substring that matches the current query (`{ tokenizedDisplayName?, fallback? }`). Useful inside a custom `user` row.

### Theming mention rows

Mention rows pick up styles from the `messageComposer.suggestions.mention` subtree:

```ts
messageComposer: {
  suggestions: {
    mention: {
      avatarSize: number;
      column: ViewStyle;
      container: ViewStyle;
      // Applied to the user mention row (`MentionUserItem`)
      name: TextStyle;
      tag: TextStyle;
      // Applied to broadcast/role/user_group rows
      enhancedMentionContainer: ViewStyle;
      enhancedMentionIcon: ViewStyle;
      enhancedMentionTitle: TextStyle;
      enhancedMentionSubtitle: TextStyle;
    }
  }
}
```

`name` and `tag` still drive user-mention typography — they are kept separate from `enhancedMention*` so user-mention rows and broadcast/role/group rows can be themed independently.

```tsx
const themeStyle = {
  messageComposer: {
    suggestions: {
      mention: {
        enhancedMentionTitle: {
          fontWeight: "600",
          fontSize: 16,
        },
        enhancedMentionSubtitle: {
          color: "#7a7a7a",
        },
      },
    },
  },
};

<Chat style={themeStyle}>{/* ... */}</Chat>;
```

### Theming rendered mention text

When a sent message is rendered, every mention token (`@<user>`, `@channel`, `@here`, `@<role>`, `@<group>`) is colored using one of four per-type semantic tokens:

| Token                      | Used for               |
| -------------------------- | ---------------------- |
| `chatTextMentionUser`      | `@<user>` mentions     |
| `chatTextMentionBroadcast` | `@channel` and `@here` |
| `chatTextMentionRole`      | `@<role>` mentions     |
| `chatTextMentionGroup`     | `@<group>` mentions    |

Matching background tokens (`chatBgMentionUser`, `chatBgMentionBroadcast`, `chatBgMentionRole`, `chatBgMentionGroup`) are available for any UI that wants a background swatch per mention type.

All per-type tokens fall back to the umbrella `chatTextMention` / `chatBgMention` by default, so existing themes look identical until you opt in to per-type coloring. Override them via the `semantics` block of your theme:

```tsx
const themeStyle = {
  semantics: {
    chatTextMentionBroadcast: "#e62e2e",
    chatTextMentionRole: "#9c27b0",
    chatTextMentionGroup: "#0c8d3a",
  },
};

<Chat style={themeStyle}>{/* ... */}</Chat>;
```

Pressing a rendered mention fires `onPressMessage` with `emitter: "textMention"` and an `additionalInfo.mentionedEntity` that carries the same `mentionType` discriminator. See [Custom message press handler](https://getstream.io/chat/docs/sdk/react-native/core-components/channel/#custom-message-press-handler) for the payload shape.

### 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:

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


---

This page was last updated at 2026-09-09T16:01:22.527Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/guides/customize-autocomplete-suggestion-list/](https://getstream.io/chat/docs/sdk/react-native/guides/customize-autocomplete-suggestion-list/).