# Search

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

Search finds channels, people and messages. In the React Native SDK, search lives in the state layer: `stream-chat` provides a `SearchController` and one search source per kind of result, and the SDK's own screens use sources where they search. The SDK has no search screen for channels, users or messages. This page covers where the SDK searches, what it leaves to your app, and how to build a search screen from the sources. How the sources query and paginate is on [Search sources](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/).

## What users get

Out of the box, users search in these places:

- **Channel details.** The full member list has a search field that narrows the members by name. The Add form searches users by name to add them. The Pinned Messages list has a search field that matches the text of the pinned messages. See [Channel details and members](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/channel-details-and-members/).
- **The composer.** Typing `@` suggests people to mention, and `/` suggests commands. Each list loads as the user types. See [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/).

The SDK doesn't have:

- a search field above the channel list, or a `Search` component;
- a screen of channel, user or message results, or components for those results;
- a `SearchController` of its own. `Chat` creates none and takes none.

To let users search their channels, people or messages, build the screen from the sources, as [Building a search screen](#building-a-search-screen) does.

## How it works across the layers

### State: the search sources

A search source holds the results of one kind of query, page by page, in its `state` store. A `SearchController` holds the search text and passes it to the sources that are active. The results are a snapshot: unlike a channel list, they don't gain or lose items as events arrive. [Search sources](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/) explains the controller, the built-in sources and how each one queries.

`stream-chat` provides four sources for chat data:

| Source                      | Searches                                                                       |
| --------------------------- | ------------------------------------------------------------------------------ |
| `ChannelSearchSource`       | The user's channels whose name matches the text. The results aren't watched.   |
| `UserSearchSource`          | Users whose id or name matches the text, without the current user.             |
| `MessageSearchSource`       | Messages that contain the text, in the user's channels, newest first.          |
| `ChannelMemberSearchSource` | The members of one channel whose name matches the text, or whose id equals it. |

### UI: where the SDK searches

Each place that searches creates its own source when it mounts, and takes yours instead as `searchSource`:

| Where                      | Source                                                                          | Takes `searchSource`                                                                                                                       |
| -------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| The member list            | `ChannelMemberSearchSource`, matching `name`, sorted by name                    | `ChannelMemberList`, `ChannelMemberListProvider`                                                                                           |
| The Add form               | `UserSearchSource`, matching `name`, sorted by name                             | `ChannelAddMembersForm`, `ChannelAddMembersProvider`                                                                                       |
| Pinned Messages            | `MessageSearchSource`, limited to the channel and `pinned: true`, matching text | `PinnedMessageList`, `ChannelPinnedMessageListProvider`                                                                                    |
| Photos & Videos, and Files | `MessageSearchSource`, limited to the channel and the attachment types          | `MediaList`, `FileAttachmentList`, and their providers                                                                                     |
| Composer suggestions       | The mentions and commands sources of the message composer                       | The composer's middleware, see [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/#replacing-mention-search) |

The channel details sources load 25 items a page, query with an empty text too, so the lists fill before the user types, and keep the current results on screen while a new text loads. The search field they render is `SearchInput`, which is exported for screens of your own.

### Opening a result

On a screen of your own, each kind of result opens differently:

- **A channel result** is a `Channel` the search loaded with its latest messages without watching it. Pass it to your channel screen. `Channel` watches it when it mounts, so the open channel receives events.
- **A message result** is a `MessageResponse` with its `cid`. `MessageSearchSource` loads the channels of its results into `client.channelManager` without watching them, so `client.channelManager.get(message.cid)` returns the channel. Open the channel and jump to the message, as [Jumping when a screen opens](https://getstream.io/chat/docs/sdk/react-native/v10/ui/layout/moving-between-channels-threads-and-messages/#jumping-when-a-screen-opens) shows.
- **A user result** is a `UserResponse`. Get the direct message channel with `client.channel()` and the two members, and open it. `Channel` watches it, which creates it on the server when it doesn't exist yet.

```ts
import type { StreamChat, UserResponse } from "stream-chat";

// The direct message channel between the current user and `user`.
export const getDirectMessageChannel = (
  client: StreamChat,
  user: UserResponse,
) =>
  client.channel("messaging", {
    members: [{ user_id: client.userId ?? "" }, { user_id: user.id }],
  });
```

## Turning it on or off

The search fields in channel details and the composer suggestions are always on where those screens render. Search for channels, users and messages is on wherever your app renders a search screen.

The SDK checks no capabilities for search. The server decides what each query returns, and message search covers only the channel types whose `search` setting is on; see [Channel types](https://getstream.io/chat/docs/javascript/channel-features/).

## Customizing

### Building a search screen

This screen searches the user's messages. It creates a `MessageSearchSource`, passes the typed text to it, reads its state with `useStateStore`, and loads the next page as the user scrolls:

```tsx title="src/MessageSearchScreen.tsx"
import { useEffect, useMemo, useState } from "react";
import {
  ActivityIndicator,
  FlatList,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
} from "react-native";
import { MessageSearchSource } from "stream-chat";
import type { MessageResponse, SearchSourceState } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react-native";

// Declared at module scope, so the subscription isn't recreated on every render.
const resultsSelector = ({
  hasNext,
  isLoading,
  items,
}: SearchSourceState<MessageResponse>) => ({ hasNext, isLoading, items });

export const MessageSearchScreen = ({
  onSelectMessage,
}: {
  onSelectMessage: (message: MessageResponse) => void;
}) => {
  const { client } = useChatContext();
  const [text, setText] = useState("");
  const source = useMemo(() => new MessageSearchSource(client), [client]);
  const { hasNext, isLoading, items } = useStateStore(
    source.state,
    resultsSelector,
  );

  // An inactive source doesn't query, and releases the channels of its results.
  useEffect(() => {
    source.activate();
    return () => {
      source.cancelScheduledQuery();
      source.deactivate();
    };
  }, [source]);

  const onChangeText = (value: string) => {
    setText(value);
    // The source waits for the user to stop typing before it queries.
    void source.search(value);
  };

  return (
    <View style={styles.container}>
      <TextInput
        accessibilityLabel="Search messages"
        onChangeText={onChangeText}
        placeholder="Search messages"
        style={styles.input}
        value={text}
      />
      <FlatList
        data={items ?? []}
        keyExtractor={(message) => message.id}
        ListFooterComponent={isLoading ? <ActivityIndicator /> : null}
        onEndReached={() => {
          // Without a text, `search()` loads the next page of the current one.
          if (hasNext && items?.length) void source.search();
        }}
        renderItem={({ item }) => (
          <Pressable
            accessibilityRole="button"
            onPress={() => onSelectMessage(item)}
            style={styles.row}
          >
            <Text style={styles.author}>
              {item.user?.name ?? item.user?.id}
            </Text>
            <Text numberOfLines={2}>{item.text}</Text>
          </Pressable>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  author: { fontWeight: "600" },
  container: { flex: 1 },
  input: { borderBottomWidth: StyleSheet.hairlineWidth, padding: 16 },
  row: { padding: 16 },
});
```

`onSelectMessage` gets the message. Your app opens its channel, `client.channelManager.get(message.cid)`, and jumps to it. To search channels or users on the same screen, add a `ChannelSearchSource` and a `UserSearchSource`, and let a `SearchController` pass the text to the one the user picks, as [The search controller](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/#the-search-controller) shows.

Create the source once per client, as `useMemo` does here. A new source on every render starts every search from scratch. To change what a source queries, such as its filters and sort, configure it; see [Built-in sources](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/#built-in-sources).

### Changing what channel details searches

Pass a source of your own as `searchSource`. The SDK uses it as it is, so activate it and set its sort yourself. The Add form renders from the `ChannelAddMembersForm` key, so this override makes it search only users whose role is `user`:

```tsx title="src/addMembersOverrides.tsx"
import { useState } from "react";
import { UserSearchSource } from "stream-chat";
import {
  ChannelAddMembersForm,
  useChatContext,
} from "stream-chat-react-native";
import type {
  ChannelAddMembersFormProps,
  ComponentOverrides,
} from "stream-chat-react-native";

const AddMembersForm = (props: ChannelAddMembersFormProps) => {
  const { client } = useChatContext();
  // Created once, when the form opens.
  const [searchSource] = useState(() => {
    const source = new UserSearchSource(client, {
      allowEmptySearchString: true,
      filters: { role: "user" },
      pageSize: 25,
      resetOnNewSearchQuery: false,
      sort: [{ direction: 1, field: "name" }],
    });
    source.activate();
    return source;
  });

  return <ChannelAddMembersForm {...props} searchSource={searchSource} />;
};

// Pass these to WithComponents around ChannelDetails.
export const addMembersOverrides: ComponentOverrides = {
  ChannelAddMembersForm: AddMembersForm,
};
```

The `UserSearchSource` matches the text against the user's id and name, and adds `filters` under that match.

### Searching your own data

A source of your own searches anything your app has, such as projects or documents. Write it as [Adding your own source](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/#adding-your-own-source) describes, and render its `state` the way the screen above renders the messages.

## Reference

- [Search sources](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/): the controller, the sources and their state.
- [Channel details lists](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel-details/channel-details-lists/): the `searchSource` props of the member list, the Add form and the message lists.
- [Channel details contexts](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel-details/channel-details-contexts/): the providers that hold each list's source.
- [Buttons and inputs](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/base-ui/buttons-and-inputs/): `SearchInput`.
- [Suggestions](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/suggestions/): the composer's mention and command search.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/search/](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/search/).