# Moving between channels, threads and messages

> **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 SDK's components take users from one place to another in many ways: a row in a list, the replies button, a search result, a quoted message. This page lists each one, with what opens and where it lands.

The components open channels and threads through [Navigation](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/navigation/), and `ChatView` decides where they land. The examples use the app from [Add threads](https://getstream.io/chat/docs/sdk/react/v15/getting-started/quick-start/#add-threads) in the Quick start: a channels view with a `channel` slot and a `thread` slot, and a threads view with a `thread` slot.

| What the user clicks                             | What opens                               | Where it lands                               |
| ------------------------------------------------ | ---------------------------------------- | -------------------------------------------- |
| **Channels** or **Threads** in the view selector | The other view                           | The whole layout                             |
| A row in the channel list                        | The channel                              | Channels view, in place of the open channel  |
| A row in the thread list                         | The thread                               | Threads view, in place of the open thread    |
| The replies button, or a message's reply action  | The message's thread                     | The view shown, beside the channel           |
| **View** on "Also sent in channel", in a thread  | The reply in the channel                 | Channels view, in the channel's message list |
| **View** on "Replied to a thread", in a channel  | The reply in its thread                  | The view shown, beside the channel           |
| A channel in the search results                  | The channel                              | Channels view, in place of the open channel  |
| A message in the search results                  | The message, in its channel              | Channels view, in place of the open channel  |
| A user in the search results                     | A direct message channel with the user   | Channels view, in place of the open channel  |
| **Send direct message** on a member's profile    | A direct message channel with the member | Channels view, in place of the open channel  |
| A pinned message in channel details              | The message, in its channel              | Channels view, in the channel's message list |
| A quoted message                                 | The quoted message                       | The same message list                        |
| The unread messages notification                 | The first unread message                 | The same message list                        |
| The scroll to latest button                      | The latest message                       | The same message list                        |
| The back button in a header                      | What the panel was opened on top of      | The same panel                               |
| The close button in a header                     | Nothing: the panel closes                |                                              |
| A section in channel details                     | That section                             | Channel details                              |

## Switching between the channel list and the thread list

`ChatView` has two views. In the Quick start app, the channels view shows the channel list and the open channel, and the threads view shows the user's threads and the open thread. `ChatView.Selector` renders a button for each, and the **Threads** button shows how many threads have unread replies.

Each view keeps what's open in its slots. A user who switches to the threads view and back finds the same channel open.

Two kinds of navigation also switch views:

- **Opening a channel switches to the channels view first.** For example, **View** on a reply in the threads view opens its channel in the channels view.
- **A thread opens in the view that's shown.** From the replies button in a channel, it opens in the channels view, beside the channel. From the thread list, it opens in the threads view.

To switch views from your own component, call `setActiveView` from `useChatViewContext()`:

```tsx
import { useChatViewContext } from "stream-chat-react/slot-layout";

export const AllThreadsButton = () => {
  const { activeView, setActiveView } = useChatViewContext();

  if (activeView === "threads") return null;

  return (
    <button onClick={() => setActiveView("threads")} type="button">
      All threads
    </button>
  );
};
```

The two buttons are also exported, as `ChatViewChannelsSelectorButton` and `ChatViewThreadsSelectorButton`, for a rail of your own. See [View selector](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/chatview-components/#view-selector).

## Opening a thread

Three things open a thread:

- **The replies button** under a message that has replies, such as "3 replies".
- **The reply actions** on a message: the quick reply button next to it, and **Thread Reply** in its menu. Replies inside a thread don't offer them.
- **A row in the thread list.** With ⌘ or ctrl held, it opens the thread beside the one that's open, when the view has a second thread slot.

Each one calls `openThread`. The replies button and the actions pass the channel and the parent message, and get the thread for that message. In the Quick start app, the thread opens in the `thread` slot, beside the channel.

The actions and the replies button use `handleOpenThread` from the message context, which `Message` builds with `useOpenThreadHandler`, so `Message`'s `openThread` prop replaces what all of them do. A custom message UI opens the thread the same way:

```tsx
import { useMessageContext } from "stream-chat-react";

export const ReplyInThreadButton = () => {
  const { handleOpenThread } = useMessageContext();

  return (
    <button onClick={handleOpenThread} type="button">
      Reply in thread
    </button>
  );
};
```

The replies button and `handleOpenThread` don't pass the click to `openThread`, so a navigation override can't read ⌘ or ctrl from them.

## Reply also sent in channel

A reply sent from a thread with **Also send in channel** checked shows in both message lists. In a channel of two members, the checkbox reads **Also send as a direct message**. In the thread, it's marked "Also sent in channel". In the channel, it's marked "Replied to a thread". **View**, next to either label, goes to the other copy:

- **From the thread to the channel.** **View** jumps to the reply in the channel's message list. If the channel isn't on screen, for example in the threads view, it opens the channel first.
- **From the channel to the thread.** **View** opens the reply's thread and jumps to the reply in it. If the thread isn't loaded yet, it fetches it first. If the thread can't be found, the user sees a "Thread has not been found" notification.

`useMessageAlsoSentInChannelNavigation()` holds this navigation. It returns `viewReference`, which picks the direction from where it's rendered, `viewReplyInChannel` and `viewReplyInThread` for each direction, and `isInThread` and `isShownInChannel`. To add something around the jump, pass your own `onView` to the SDK's indicator and replace it through `WithComponents`:

```tsx
import type { PropsWithChildren } from "react";
import {
  MessageAlsoSentInChannelIndicator,
  useMessageAlsoSentInChannelNavigation,
  WithComponents,
} from "stream-chat-react";

const LoggedAlsoSentIndicator = () => {
  const { isInThread, viewReference } = useMessageAlsoSentInChannelNavigation();

  return (
    <MessageAlsoSentInChannelIndicator
      onView={async () => {
        console.log(isInThread ? "To the channel" : "To the thread");
        await viewReference();
      }}
    />
  );
};

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

On a narrow screen, the thread can cover the channel it jumps to. [Responsive layouts](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/responsive-layouts/#deciding-what-to-show) uses this override to close the thread first.

## Opening a channel from elsewhere

Besides a row in the channel list, four things open a channel:

- **A channel in the search results.** It opens the channel, watches it, and adds it to the channel lists whose filters it matches, so it shows there without reloading them.
- **A message in the search results.** It opens the message's channel and jumps to the message.
- **A user in the search results.** It opens a direct message channel with that user, creating it if it doesn't exist. The channel type is `Search`'s `directMessagingChannelType`, `messaging` by default.
- **Channel details.** **Send direct message** on a member's profile opens a direct message channel with the member, in the channel's type. Selecting a pinned message opens its channel and jumps to the message. Both close channel details.

A plain click on a channel replaces the open channel, and closes the thread that was open beside it. The channel list and the search results pass the click to `openChannel`, so an override can open the channel beside instead: see [⌘/ctrl-click opens beside](https://getstream.io/chat/docs/sdk/react/v15/recipes/layout/ctrl-click-opens-beside/). Each row and result also takes an `onSelect` handler, which replaces what a click does.

The two actions in channel details call `openChannel` too, without the click, so an override passed as `deriveWorkspaceNavigation`, or a navigation of your own, receives them with no event.

## Jumping to a message

A jump moves a message list to one message. When that message isn't loaded, the list loads the messages around it first. The message is then scrolled into view and highlighted. The SDK jumps to a message from:

- **A quoted message.** Clicking the quote jumps to the original, in the list the quote is in: the thread's list inside a thread.
- **A message in the search results**, after opening its channel.
- **A pinned message** in channel details, after opening its channel.
- **A reply also sent in channel**, in either direction.
- **The unread messages notification** in a channel's message list, which jumps to the first unread message.
- **The scroll to latest button**, which jumps to the latest message when it isn't loaded, and otherwise scrolls down to it.

The jump itself is the message list's, in `stream-chat`: see [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/). A component of your own jumps the same way, in the list on screen: `useMessagePaginator()` returns the thread's list inside a thread, and the channel's otherwise.

```tsx
import { useMessagePaginator } from "stream-chat-react";

export const ShowMessageButton = ({ messageId }: { messageId: string }) => {
  const messagePaginator = useMessagePaginator();

  return (
    <button
      onClick={() => void messagePaginator.jumpToMessage(messageId)}
      type="button"
    >
      Show message
    </button>
  );
};
```

To jump in a channel that isn't open, open it first with `openChannel`, then jump, as the message search results do.

## Back and close

`ChannelHeader` and `ThreadHeader` have a back button at the start and a close button at the end. They are `WorkspaceNavigationBackButton` and `WorkspaceNavigationCloseButton`, the defaults of each header's `StartContent` and `EndContent`. Each one shows only when it can do something:

- **Back** shows on a panel that was opened on top of something else. For example, a channel opened beside the open one while a thread is there covers that thread. Back reveals the thread again. If it was the only layer, the thread mounts again and loses its scroll position. See [Layers](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#layers).
- **Close** on a thread shows beside a channel, and on a second thread in the threads view. The threads view's main thread has none: the user switches views instead.
- **Close** on a channel shows only on a channel opened beside another one.

Closing a panel beside the main one closes it together with everything opened on top of it. In the main panel, close removes what's on top, one layer at a time. With nothing stacked on it, close empties the main panel, for example a thread that replaced the channel in a one-slot layout.

`useCloseThread()` returns the function the thread's close button calls. It closes the thread in context, in the panel it renders in. A custom thread header uses it like this:

```tsx
import {
  ThreadHeader,
  useCloseThread,
  useThreadContext,
  useWorkspaceNavigation,
  useWorkspacePanel,
} from "stream-chat-react";

const DoneButton = () => {
  const thread = useThreadContext();
  const panel = useWorkspacePanel();
  const { isThreadDismissable } = useWorkspaceNavigation();
  const closeThread = useCloseThread();

  if (!isThreadDismissable(thread?.id, panel)) return null;

  return (
    <button onClick={(event) => closeThread({ event })} type="button">
      Done
    </button>
  );
};

export const MyThreadHeader = () => <ThreadHeader EndContent={DoneButton} />;
```

A component passed as `EndContent` or `StartContent` replaces the SDK's button, so it checks `isThreadDismissable` itself.

## Inside channel details

`AvatarWithChannelDetail`, from `stream-chat-react/channel-detail`, makes the channel header's avatar open channel details:

```tsx
import { ChannelHeader } from "stream-chat-react";
import { AvatarWithChannelDetail } from "stream-chat-react/channel-detail";

export const DetailedChannelHeader = () => (
  <ChannelHeader Avatar={AvatarWithChannelDetail} />
);
```

Channel details has five sections: **Channel info**, **Members**, **Pinned messages**, **Photos & videos** and **Files**. Its section navigator moves between them, in one of two layouts:

- **Wide**, when channel details are at least 640px wide: the sections are listed beside the one shown, and picking one shows it in its place.
- **Narrow**: one column. The section's header has a menu button that opens the list of sections as a drawer. Escape or a click outside closes it.

Some sections open a screen of their own with a back button: a member's profile from **Members**, adding members, and editing the channel info. Back returns to the screen it was opened from. A header with a back button shows no menu button.

Selecting a pinned message and **Send direct message** leave channel details, as described in [Opening a channel from elsewhere](#opening-a-channel-from-elsewhere). The sections and the navigator's props are in [Section Navigator](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/section-navigator/).

## Used in

- [Replies and threads](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/replies-and-threads/)
- [Search](https://getstream.io/chat/docs/sdk/react/v15/features/channels/search/)
- [Channel details and members](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-details-and-members/)
- [Channel lists](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/ui/layout/moving-between-channels-threads-and-messages/](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/moving-between-channels-threads-and-messages/).