# Search customization

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

The search bar above the channel list finds channels, people and messages. This recipe changes the search bar's settings, replaces how channel results look, and moves search out of the sidebar into a top bar. How search works across the layers, and what each result does when clicked, is on [Search](https://getstream.io/chat/docs/sdk/react/v15/features/channels/search/).

## Best practices

- Override the smallest part you need: `Search` to set its props, `SearchSourceResultList` to change one source's results, `SearchBar` or `SearchResults` for the rest.
- Start from `DefaultSearchResultItems` and replace one entry, so the other sources keep their default results.
- Keep what a result does when clicked. A channel result opens the channel, watches it, and adds it to the channel lists that match it.
- Keep `role="option"` on a custom result's button. The arrow keys move between the results that have it.

## Configure search through `WithComponents`

`ChannelNavigation` renders the channel list header, the search bar and the channel lists. It renders search from the `Search` key, so to change the search bar's props, set a component on that key that renders `Search` with them:

```tsx title="src/Sidebar.tsx"
import { ChannelNavigation, Search, WithComponents } from "stream-chat-react";

const SidebarSearch = () => (
  <Search
    directMessagingChannelType="team"
    exitSearchOnInputBlur
    placeholder="Search teammates, channels and messages"
  />
);

const overrides = { Search: SidebarSearch };

export const Sidebar = () => (
  <WithComponents overrides={overrides}>
    <ChannelNavigation />
  </WithComponents>
);
```

- `directMessagingChannelType` is the channel type a user result opens; the default is `messaging`.
- `exitSearchOnInputBlur` closes search when focus moves from the input to something outside search, such as a click elsewhere.
- `placeholder`, `disabled` and `inputProps` go to the input.

The key applies only where the SDK renders search. A `Search` you render yourself takes the props directly, as in [Render search outside ChannelList](#render-search-outside-channellist).

## Customize search result items

`SearchSourceResultList` renders the results of one source. Its `SearchResultItems` prop maps each source's `type` (`channels`, `users`, `messages`) to the component that renders one result. This list renders channel results as compact rows with the channel's name and member count, and keeps the default user and message results:

```tsx title="src/CompactChannelResults.tsx"
import type { PropsWithChildren } from "react";
import type { Channel, ChannelStateData } from "stream-chat";
import {
  DefaultSearchResultItems,
  SearchSourceResultList,
  useChannelDisplayName,
  useChatContext,
  useStateStore,
  useWorkspaceNavigation,
  WithComponents,
} from "stream-chat-react";

const memberCountSelector = ({ memberCount }: ChannelStateData) => ({
  memberCount,
});

const CompactChannelResult = ({ item }: { item: Channel }) => {
  const { channelManager, client } = useChatContext();
  const { openChannel } = useWorkspaceNavigation();
  const name = useChannelDisplayName(item);
  const { memberCount } = useStateStore(item.state, memberCountSelector);

  return (
    <button
      className="channel-result"
      onClick={(event) => {
        openChannel(item, { event });
        // Channel search doesn't watch its results, so watch the channel to receive its events.
        item.watch().catch((error: Error) =>
          client.notifications.addError({
            message: "Failed to load the channel",
            options: { originalError: error },
            origin: { emitter: "CompactChannelResult" },
          }),
        );
        // Add the channel to the channel lists whose filters it matches.
        channelManager.ingestChannel(item);
      }}
      role="option"
      type="button"
    >
      <span className="channel-result__name"># {name}</span>
      <span className="channel-result__members">{memberCount} members</span>
    </button>
  );
};

const searchResultItems = {
  ...DefaultSearchResultItems,
  channels: CompactChannelResult,
};

const ResultList = () => (
  <SearchSourceResultList SearchResultItems={searchResultItems} />
);

const overrides = { SearchSourceResultList: ResultList };

export const WithCompactChannelResults = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);
```

The click does what the default channel result does:

1. `openChannel` hands the channel to the layout, which decides where it opens. The event is passed on, so ⌘/ctrl-click can open it beside the current one.
2. `Channel.watch()` watches it. A failed watch publishes an error notification, as the default does.
3. `ChannelManager.ingestChannel()` adds it to the registered channel lists that match it, so it shows in the list without a new query.

To keep the default result and only add to it, wrap it instead. `ChannelSearchResultItem`, `UserSearchResultItem` and `MessageSearchResultItem` are exported, and each takes `onSelect` to replace its click. The [Search](https://getstream.io/chat/docs/sdk/react/v15/features/channels/search/) feature page wraps the default user result this way.

## Render search outside ChannelList

`Search` reads the search controller from `Chat`, so it works anywhere inside `Chat`. This workspace puts it in a top bar and keeps the channel lists in the sidebar:

```tsx title="src/Workspace.tsx"
import {
  ChannelHeader,
  ChannelLists,
  MessageComposer,
  MessageList,
  Search,
  ThreadHeader,
} from "stream-chat-react";
import {
  ChannelSlot,
  ChatView,
  ThreadSlot,
} from "stream-chat-react/slot-layout";
import type { LayoutDescriptor } from "stream-chat-react/slot-layout";
import "./Workspace.css";

const layouts: LayoutDescriptor[] = [
  { id: "channels", slots: ["channel", "thread"] },
];

const ChannelsView = () => (
  <>
    <header className="top-bar">
      <Search placeholder="Search everything" />
    </header>
    <aside className="sidebar">
      <ChannelLists />
    </aside>
    <ChannelSlot slot="channel">
      <ChannelHeader />
      <MessageList />
      <MessageComposer />
    </ChannelSlot>
    <ThreadSlot slot="thread">
      <ThreadHeader />
      <MessageList />
      <MessageComposer />
    </ThreadSlot>
  </>
);

export const Workspace = () => (
  <ChatView layouts={layouts} views={{ channels: <ChannelsView /> }} />
);
```

```css title="src/Workspace.css"
.top-bar {
  position: relative;
}

/* The results open under the search bar, over the content below it. */
.top-bar .str-chat__search-results {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  z-index: 10;
  max-height: 60vh;
  overflow-y: auto;
  background: #ffffff;
}
```

`ChannelLists` renders every list registered on `client.channelManager`. Unlike `ChannelNavigation`, it doesn't hide the lists while search is open, so the sidebar stays as it is and the results show in the top bar. The results render inside `Search`, under its search bar, so the CSS above lifts them over the content as a dropdown. [Workspace with ChatView](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/) explains `layouts`, views and slots.

## Go lower-level when you need custom data sources

The components above change how search looks. What it searches comes from the `SearchController` that `Chat` holds, with one source per kind of result:

- To search fewer kinds of results, or to change a source's filters, pass your own controller to `Chat` as `searchController`. The [Search](https://getstream.io/chat/docs/sdk/react/v15/features/channels/search/) feature page shows how, under "Choosing what to search".
- To search your own data, such as projects or documents, write a source as [Adding your own source](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/search-sources/#adding-your-own-source) describes, add it to the controller, and map its `type` in `SearchResultItems` as above.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/recipes/channel-list/search-customization/](https://getstream.io/chat/docs/sdk/react/v15/recipes/channel-list/search-customization/).