# ChatView components

> **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/).

`ChatView` is the SDK's layout. It decides where an opened channel or thread goes, and the components on this page render what it placed. [Workspace with ChatView](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/) explains how they work together.

Everything on this page imports from `stream-chat-react/slot-layout`.

## ChatView

The root of the layout. It holds the layout's state, provides [workspace navigation](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/workspace-navigation-api/) to the components inside it, and renders the shown view. What it renders depends on its props:

- **With `views`:** its `children`, then the shown view's content.
- **With `layout="nav-rail-entity-list-workspace"` and no `views`:** the built-in workspace layout, the view selector in a rail, then every slot of the shown view rendered through `slotRenderers`. Its `children` aren't rendered.
- **With neither:** its `children`.

Its root element has the classes `str-chat`, the `theme` from `Chat`, and `str-chat__chat-view`.

| Prop                        | Type                                                       | Default                                                     | Description                                                                                                                                                                                                                                                                                               |
| --------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children`                  | `ReactNode`                                                |                                                             | With `views`, rendered in every view, before the shown view's content: put components that must always run here, such as [`DisposedChannelRelease`](#disposedchannelrelease). With `layout` and no `views`, not rendered. With neither, the whole content.                                                |
| `deriveWorkspaceNavigation` | `DeriveWorkspaceNavigation`                                |                                                             | Replaces members of the navigation that `ChatView` provides. See [Overriding navigation](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/workspace-navigation-api/#overriding-navigation). Keep it stable.                                                                                            |
| `dialogManagerId`           | `string`                                                   | a local manager                                             | Registers the dialog manager `ChatView` hosts inside its root under this id. Dialogs opened by the views' content render there, inside `.str-chat`. See [Dialog managers](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/dialogs/#dialog-managers).                                                              |
| `duplicateEntityPolicy`     | `"allow" \| "move" \| "reject"`                            | `"allow"`                                                   | What happens when a channel or thread is opened into a slot while another slot holds it. See [Duplicates](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/#duplicates). A new value starts the layout again.                                                                              |
| `entityInferrers`           | `ChatViewEntityInferer[]`                                  |                                                             | No effect: `ChatView` accepts it, but nothing reads it.                                                                                                                                                                                                                                                   |
| `layout`                    | `"nav-rail-entity-list-workspace"`                         |                                                             | Renders the [built-in workspace layout](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#the-built-in-workspace-layout). Ignored when `views` is passed.                                                                                                                                       |
| `layoutController`          | `LayoutController`                                         | one `ChatView` creates                                      | A layout controller to use instead. `layouts`, `duplicateEntityPolicy` and `resolveDuplicateEntity` set up the controller `ChatView` creates, so they have no effect with this prop. The `LayoutController` class isn't exported, only its type.                                                          |
| `layouts`                   | `LayoutDescriptor[]`                                       | one slot per view, `slot1`, holding its list                | Each view's slots, in order, and what they hold at the start. See [`LayoutDescriptor`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/#layoutdescriptor). Without `views` or `layout`, the default is one empty `slot1`, in the channels view only. A new array starts the layout again. |
| `resolveDuplicateEntity`    | `ResolveDuplicateEntity`                                   |                                                             | Decides the duplicate policy per open, instead of `duplicateEntityPolicy`. A new function starts the layout again.                                                                                                                                                                                        |
| `SlotFallback`              | `ComponentType<{ slot: string }>`                          |                                                             | Built-in layout only. Rendered in a slot that holds nothing, or whose renderer returns nothing. Without it, such a slot is blank, and while every slot is blank the layout shows [`ChatViewEmptyPlaceholder`](#chatviewemptyplaceholder).                                                                 |
| `slotFallbackComponents`    | `Partial<Record<string, ComponentType<{ slot: string }>>>` |                                                             | Built-in layout only. `SlotFallback` per slot name. Wins over `SlotFallback`.                                                                                                                                                                                                                             |
| `slotRenderers`             | `ChatViewSlotRenderers`                                    | renderers for `channelList`, `threadList` and `userProfile` | How `Slot` and the built-in layout render each kind of thing a slot holds. See [`slotRenderers`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/#slotrenderers). Keep it stable.                                                                                                         |
| `viewActionSlotResolvers`   | `Partial<Record<ChatView, ViewActionSlotResolvers>>`       |                                                             | Picks the slot for an opened channel or thread, per view. See [`viewActionSlotResolvers`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/#viewactionslotresolvers). Keep it stable.                                                                                                      |
| `views`                     | `{ channels?: ReactNode; threads?: ReactNode }`            |                                                             | The content of each view. The shown view's content renders in a `div` with the class `str-chat__chat-view__channels` or `str-chat__chat-view__threads`. The views are `channels` and `threads` only: the `ChatView` type is `"channels" \| "threads"`.                                                    |

"Keep it stable" means declaring the value outside the component, or memoizing it. [Views and slots](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#views-and-slots) explains why `layouts` has to be stable too.

### useChatViewContext

`useChatViewContext()` reads the `ChatView` around the component. It throws outside a `ChatView`.

| Member                            | Type                                                                             | Description                                                                                                                                                             |
| --------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `activeView`                      | `"channels" \| "threads"`                                                        | The view that's shown.                                                                                                                                                  |
| `setActiveView`                   | `(view: ChatView) => void`                                                       | Shows a view. Each view keeps what its slots hold.                                                                                                                      |
| `layoutController`                | `LayoutController`                                                               | The layout's state and the methods that change it. See [The layout controller](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/#the-layout-controller). |
| `registerViewActionSlotResolvers` | `(view: ChatView, resolvers?: ViewActionSlotResolvers) => void`                  | Sets the slot resolvers of a view, as the `viewActionSlotResolvers` prop does. `undefined` removes them.                                                                |
| `resolveActionTargetSlot`         | `(view: ChatView, args: ResolveViewActionTargetSlotArgs) => string \| undefined` | Runs the view's resolver for `args.action`.                                                                                                                             |
| `entityInferers`                  | `ChatViewEntityInferer[]`                                                        | The `entityInferrers` prop. Nothing reads it.                                                                                                                           |

`ChatViewContext` itself is exported too. `useContext(ChatViewContext)` returns `undefined` outside a `ChatView` instead of throwing, which suits a component that also renders without one.

[Switching between the channel list and the thread list](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/moving-between-channels-threads-and-messages/#switching-between-the-channel-list-and-the-thread-list) has an example of `setActiveView`. For opening and closing channels and threads, use [`useWorkspaceNavigation()`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/workspace-navigation-api/) instead: it works with any layout.

### DisposedChannelRelease

`DisposedChannelRelease` empties every slot of the shown view whose channel `stream-chat` disposed of: deleted, or the current user removed from it. It takes no props and renders nothing. It's opt-in: render it once inside `ChatView`, as one of its `children`. The built-in layout doesn't render `ChatView`'s `children`, so it can't host it. [Disposed channels](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#disposed-channels) explains when a channel is disposed of.

## WorkspaceLayout

A frame of slots: an optional rail, then a row of slots. The built-in workspace layout is a `WorkspaceLayout`, and a view's content can use one too.

| Prop      | Type                    | Default  | Description                                                                                                                         |
| --------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `slots`   | `WorkspaceLayoutSlot[]` | required | The slots, in order. Each is `{ slot: string; content?: ReactNode }`, rendered as a [`Slot`](#slot) with `content` as its children. |
| `navRail` | `ReactNode`             |          | Rendered in the rail, `str-chat__chat-view__workspace-layout-nav-rail`, before the slots. Without it, there's no rail.              |

While no entry has `content` that renders something (every one is `undefined`, `null` or a boolean), `WorkspaceLayout` shows `ChatViewEmptyPlaceholder` in place of the slots. So give each slot its content. An entry without content, next to one with content, renders what its slot holds through `slotRenderers`.

```tsx
import {
  ChannelHeader,
  ChannelNavigation,
  MessageComposer,
  MessageList,
  ThreadHeader,
  ThreadList,
} from "stream-chat-react";
import {
  ChannelSlot,
  ChatView,
  ChatViewEmptyPlaceholder,
  ThreadSlot,
  WorkspaceLayout,
} from "stream-chat-react/slot-layout";
import type { LayoutDescriptor } from "stream-chat-react/slot-layout";

// "list" isn't one of the views' slots, so nothing opens there.
const layouts: LayoutDescriptor[] = [
  { id: "channels", slots: ["channel", "thread"] },
  { id: "threads", slots: ["thread"] },
];

const threadPanel = {
  content: (
    <ThreadSlot slot="thread">
      <ThreadHeader />
      <MessageList />
      <MessageComposer />
    </ThreadSlot>
  ),
  slot: "thread",
};

const ChannelsView = () => (
  <WorkspaceLayout
    navRail={<ChatView.Selector />}
    slots={[
      { content: <ChannelNavigation />, slot: "list" },
      {
        content: (
          <ChannelSlot fallback={<ChatViewEmptyPlaceholder />} slot="channel">
            <ChannelHeader />
            <MessageList />
            <MessageComposer />
          </ChannelSlot>
        ),
        slot: "channel",
      },
      threadPanel,
    ]}
  />
);

const ThreadsView = () => (
  <WorkspaceLayout
    navRail={<ChatView.Selector />}
    slots={[{ content: <ThreadList />, slot: "list" }, threadPanel]}
  />
);

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

Each view gets its content: the selector can show either view, and a view without content renders nothing, the selector included. The channels view has a `thread` slot so that a reply thread opens beside the channel. With only a `channel` slot, the thread would replace the channel there, and `ChannelSlot` would show its fallback.

### ChatViewEmptyPlaceholder

The SDK's empty state for a layout: an icon and "No chat selected", or "No thread selected" in the threads view. It takes no props. `WorkspaceLayout` shows it while no slot has content, and it works as a `fallback` or `SlotFallback`. Outside a `ChatView`, it uses the channels wording.

## Slot

Renders what one slot holds, and the [layers](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#layers) on top of it, through `slotRenderers`.

| Prop        | Type        | Default  | Description                                                                |
| ----------- | ----------- | -------- | -------------------------------------------------------------------------- |
| `slot`      | `string`    | required | The slot to render.                                                        |
| `children`  | `ReactNode` |          | Rendered instead of what the slot holds. Layers still render on top of it. |
| `className` | `string`    |          | Added to the slot element.                                                 |

It renders a `section` with the class `str-chat__chat-view__slot` and `data-slot` set to the slot name. A hidden slot also gets `str-chat__chat-view__slot--hidden` and `aria-hidden`, and stays mounted. While the slot has layers, what it holds and each layer stay mounted: only the top one shows, and the ones beneath get the `hidden` attribute. Pushing the first layer and removing the last one mount what the slot holds again, so it loses its scroll position.

`Slot` declares its slot as the panel its content renders in, so `useWorkspacePanel()` inside returns the slot name. [Panels](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/navigation/#panels) explains what the panel is for.

## Entity adapters

Three components render a slot's channel, thread or thread list with the matching SDK component, around their children. [Rendering a slot](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/#rendering-a-slot) compares them with `Slot`.

Always pass `slot`. Without it, `ChannelSlot` and `ThreadSlot` render the first channel or thread in the view, which is only right in a layout with one of each. An adapter only displays: it never moves a channel or thread from another slot.

### ChannelSlot

Renders a [`Channel`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/channel/) for the channel in its slot. It shows what the slot holds underneath any layer, never the layer.

| Prop       | Type        | Default | Description                               |
| ---------- | ----------- | ------- | ----------------------------------------- |
| `slot`     | `string`    |         | The slot to read.                         |
| `fallback` | `ReactNode` | `null`  | Rendered while the slot holds no channel. |
| `children` | `ReactNode` |         | Rendered inside the `Channel`.            |

It also takes every prop of `Channel` except `channel`, and passes them on.

### ThreadSlot

Renders a [`Thread`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/thread-list/thread/) for the thread in its slot. Like `ChannelSlot`, it shows what the slot holds underneath any layer.

| Prop          | Type        | Default | Description                                                                               |
| ------------- | ----------- | ------- | ----------------------------------------------------------------------------------------- |
| `slot`        | `string`    |         | The slot to read.                                                                         |
| `fallback`    | `ReactNode` | `null`  | Rendered while the slot holds no thread.                                                  |
| `hideIfEmpty` | `boolean`   | `true`  | Hides the slot while it holds no thread, and shows it again when one opens. Needs `slot`. |
| `children`    | `ReactNode` |         | Rendered inside the `Thread`.                                                             |

It also takes every prop of `Thread` except `thread`. `ThreadSlot` doesn't mark the thread as on screen: call [`useActiveThread`](#useactivethread) inside it.

### ThreadListSlot

Puts a thread list in a slot of the shown view, and renders that slot as a `Slot`. The slot it uses is the first of: `slot`, when the view has it; the slot that already holds a thread list; the first empty slot; the view's first slot. When `slot` is passed and another slot holds the list, it empties that one.

| Prop       | Type        | Default | Description                                                                                                         |
| ---------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `slot`     | `string`    |         | The slot to put the thread list in.                                                                                 |
| `fallback` | `ReactNode` | `null`  | Rendered when the view has no slot. While `children` aren't passed, it's also rendered in place of the thread list. |
| `children` | `ReactNode` |         | Rendered in the slot instead of the thread list.                                                                    |

Without `children` or `fallback`, the slot renders `slotRenderers.threadList`, which is `ThreadList` by default.

## View selector

`ChatView.Selector` renders the buttons that switch views, in a `role="navigation"` element labelled "Chat view controls".

| Prop       | Type                      | Default                          | Description                                              |
| ---------- | ------------------------- | -------------------------------- | -------------------------------------------------------- |
| `iconOnly` | `boolean`                 | `true`                           | Shows icons without their labels. Passed to each button. |
| `itemSet`  | `ChatViewSelectorEntry[]` | `defaultChatViewSelectorItemSet` | The buttons, in order.                                   |

Each entry is `{ Component, type }`: `Component` receives `iconOnly`, and `type` is the entry's unique key. The default set is the channels button and the threads button. A button of your own doesn't have to switch views: there are only the two, `channels` and `threads`. This set adds a button that opens a support channel:

```tsx
import { useMemo } from "react";
import { useChatContext, useWorkspaceNavigation } from "stream-chat-react";
import {
  ChatView,
  ChatViewSelectorButton,
  defaultChatViewSelectorItemSet,
} from "stream-chat-react/slot-layout";
import type {
  ChatViewSelectorEntry,
  ChatViewSelectorItemProps,
} from "stream-chat-react/slot-layout";

const SupportIcon = () => (
  <svg aria-hidden="true" viewBox="0 0 20 20">
    <circle cx="10" cy="10" fill="currentColor" r="8" />
  </svg>
);

const SupportSelectorButton = ({ iconOnly }: ChatViewSelectorItemProps) => {
  const { client } = useChatContext();
  const { isChannelActive, openChannel } = useWorkspaceNavigation();
  const channel = useMemo(
    () => client.channel("messaging", "support"),
    [client],
  );

  return (
    <ChatViewSelectorButton
      Icon={SupportIcon}
      iconOnly={iconOnly}
      isActive={isChannelActive(channel.cid)}
      onClick={(event) => {
        // openChannel places the channel; watching loads it.
        channel.watch().catch((error) => console.error(error));
        openChannel(channel, { event });
      }}
      text="Support"
    />
  );
};

const itemSet: ChatViewSelectorEntry[] = [
  ...defaultChatViewSelectorItemSet,
  { Component: SupportSelectorButton, type: "support" },
];

export const Selector = () => <ChatView.Selector itemSet={itemSet} />;
```

### The individual buttons

Render them directly when a rail of your own needs its own arrangement.

| Component                        | Props               | Description                                                                                                              |
| -------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ChatViewChannelsSelectorButton` | `iconOnly` (`true`) | Shows the channels view. Its icon is filled while that view is shown.                                                    |
| `ChatViewThreadsSelectorButton`  | `iconOnly` (`true`) | Shows the threads view, with a badge counting the threads that have unread replies. Its `aria-label` includes the count. |
| `ChatViewSelectorButton`         | see below           | The button both are built on.                                                                                            |

`ChatViewSelectorButton` takes every prop of a `button`, plus:

| Prop         | Type            | Default | Description                                                                       |
| ------------ | --------------- | ------- | --------------------------------------------------------------------------------- |
| `Icon`       | `ComponentType` |         | The icon.                                                                         |
| `ActiveIcon` | `ComponentType` |         | The icon while `isActive`. Falls back to `Icon`.                                  |
| `isActive`   | `boolean`       |         | Marks the button as the current one, with `aria-current`.                         |
| `text`       | `string`        |         | The label. With `iconOnly`, it's shown as a tooltip and used as the `aria-label`. |
| `iconOnly`   | `boolean`       | `true`  | Hides the label next to the icon.                                                 |
| `children`   | `ReactNode`     |         | Rendered instead of the icon.                                                     |

## useActiveThread

`useActiveThread({ activeThread })` marks a thread as on screen while the window is visible and focused, and unmarks it when the window is hidden or loses focus, when the component unmounts, and when `activeThread` changes. It returns nothing.

| Option         | Type                  | Description                                     |
| -------------- | --------------------- | ----------------------------------------------- |
| `activeThread` | `Thread \| undefined` | The thread on screen. `undefined` does nothing. |

While a thread is on screen, its new replies are marked read, and a thread that went out of date, after a lost connection for example, reloads. `ThreadSlot` doesn't call it, so call it from a component inside the slot:

```tsx
const ActiveThread = () => {
  const thread = useThreadContext();
  useActiveThread({ activeThread: thread });
  return null;
};
```

It doesn't need `ChatView`: [Providing your own navigation](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/navigation/#providing-your-own-navigation) calls it without one.

## See also

- [Workspace with ChatView](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/workspace-with-chatview/): views, slots, where things land, and rendering a slot.
- [Slot layout](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-layout/): the bindings, the layout controller and the hooks that read the layout.
- [Workspace navigation API](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/workspace-navigation-api/): the navigation `ChatView` provides.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/chatview-components/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/chatview-components/).