# Overview

Building chat from scratch in React Native is complex: state, styling, message grouping, read states, context menus, and more. Stream Chat for React Native provides a flexible, customizable UI so you can ship quickly and tailor the experience.

Before starting, install `stream-chat-react-native` as directed in the [getting started](/chat/docs/sdk/react-native/v8/) guide.

## Best Practices

- Create a single `StreamChat` client instance and reuse it throughout the app.
- Connect users with secure server-generated tokens; use developer tokens only for local testing.
- Use `useCreateChatClient` or manage connect/disconnect explicitly to avoid duplicate connections.
- Initialize channels once and remove setup code after creation to avoid accidental duplicates.
- Wrap UI in `OverlayProvider` and `Chat` at the highest sensible level to keep contexts consistent.

## Creating a Chat Client

Stream Chat for React Native uses [`stream-chat`](https://github.com/GetStream/stream-chat-js), Stream's JavaScript client.
`stream-chat` is included as a dependency.

<admonition type="note">

You don't have to explicitly install `stream-chat` in your project.

</admonition>

Sign up for a [Free subscription](https://getstream.io/chat/pricing/) to get your API key.

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

const client = StreamChat.getInstance("api_key");
```

## Connecting a User

Tokens authenticate users. Typically, you send the token from your backend when the user registers or logs in. See [Tokens & Authentication](/chat/docs/javascript/tokens_and_authentication/) for details. You can use a [developer token](/chat/docs/javascript/tokens_and_authentication/#developer-tokens/) for local testing.

Call `connectUser` with the user object and `user_token` after creating the client.

```ts
await client.connectUser(
  {
    id: "jlahey",
    name: "Jim Lahey",
    image: "https://i.imgur.com/fR9Jz14.png",
  },
  "user_token",
);
```

Avoid repeated `connectUser` calls; reconnecting without disconnecting will warn and can throw.

To disconnect, call `disconnectUser`.

```ts
await client.disconnectUser();
```

<admonition type="tip">

You can also use `useCreateChatClient` from `stream-chat-react-native`/`stream-chat-expo` to handle connect/disconnect automatically.

</admonition>

## Creating a Channel

Channels hold messages and interactions. Create one before rendering UI components.

A [channel type](/chat/docs/javascript/creating_channels#channel-data/) controls settings for a channel. There are 5 defaults:

- `commerce`
- `gaming`
- `livestream`
- `messaging`
- `team`

You can also define custom channel types.

A channel must be initialized with either an `id` or a list of members. If you pass members, the backend auto-generates an `id`.

<admonition type="note">

You can't add or remove members from a channel created using a members list.

</admonition>

You can add custom data as long as the total custom fields size is _less than 5KB_.

For this guide, create a channel once (by `id` or members list) and remove the setup code after.

<tabs>

<tabs-item value="channelId" label="Channel Id">

```ts
/**
 *  Channel created using a channel id
 */
const channel = client.channel("messaging", "the_park", {
  name: "The Park",
});
```

</tabs-item>

<tabs-item value="members" label="Members List">

```ts
/**
 *  Channel created using a members list
 */
const channel = client.channel("messaging", {
  members: ["jlahey", "rlafleur"],
  name: "The Park",
});
```

</tabs-item>

</tabs>

To create this channel on the server you must call `create` on the new channel instance.

```ts
await channel.create();
```

<admonition type="note">

For on-the-fly channel creation, call `watch` to create and subscribe to updates.

</admonition>

## Configuring UI Components

With a `client`, a connected user, and a channel, you can set up UI components.

All major components rely on React contexts provided by top-level `Providers`.

### Overlay Provider

`OverlayProvider` is the top-level provider. It enables message actions, the full-screen image viewer, and the `AttachmentPicker` as a keyboard-style view.

You can use it with defaults or customize via props.

For details, see the [OverlayProvider docs](/chat/docs/sdk/react-native/v8/core-components/overlay-provider/), including how to use the `OverlayProvider` with [React Navigation](https://reactnavigation.org/).

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

export const Screen = () => (
  <OverlayProvider>{/** App components */}</OverlayProvider>
);
```

### Chat

`Chat` is the next provider below `OverlayProvider` and is required. Wrap the entire app or a specific screen. It requires `client` (the `StreamChat` instance [you created](#creating-a-chat-client)).

See the [Chat component docs](/chat/docs/sdk/react-native/v8/core-components/chat/) for theming and translations.

<tabs>

<tabs-item value="app" label="App">

```tsx {7-9}
import { StreamChat } from "stream-chat";
import { Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => (
  <OverlayProvider>
    <Chat client={client}>{/** App components */}</Chat>
  </OverlayProvider>
);
```

</tabs-item>

<tabs-item value="screen" label="Screen">

```tsx
import { StreamChat } from "stream-chat";
import { Chat } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const Screen = () => (
  <Chat client={client}>{/** App components */}</Chat>
);
```

```tsx {6}
import { OverlayProvider } from "stream-chat-react-native";
import { Screen } from "./Screen";

export const App = () => (
  <OverlayProvider>
    <Screen />
  </OverlayProvider>
);
```

</tabs-item>

</tabs>

## Channel List

`ChannelList` renders channels the user can access. Provide `onSelect` to handle navigation or store the selected `Channel`.

See the [ChannelList component docs](/chat/docs/sdk/react-native/v8/core-components/channel-list/) for filtering and sorting.

Render `ChannelList` inside the providers [above](#configuring-ui-components). The example assumes the client and user are already set up.

![ChannelList](@chat-sdk/react-native/v8/_assets/basics/hello-stream-chat/channel_list.png)

```tsx {9}
import { StreamChat } from "stream-chat";
import { ChannelList, Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => (
  <OverlayProvider>
    <Chat client={client}>
      <ChannelList />
    </Chat>
  </OverlayProvider>
);
```

These components render a [`FlatList`](https://reactnative.dev/docs/flatlist) of channels for the connected user.

## Channel

`Channel` wraps the chat UI and provides contexts. It is the main entry point for customizing the chat experience.

For customization and keyboard offsets, see the [Channel component docs.](/chat/docs/sdk/react-native/v8/core-components/channel/)

`Channel` requires the `channel` prop, a `StreamChat` `Channel` instance. Create it [as above](#creating-a-channel), read it from `client.activeChannels`, or store it from `ChannelList`.

```tsx {8,11-15}
import React, { useState } from "react";
import { Channel as ChannelType, StreamChat } from "stream-chat";
import { ChannelList, Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => {
  const [channel, setChannel] = useState<ChannelType>();

  return (
    <OverlayProvider>
      <Chat client={client}>
        {channel ? (
          <Channel channel={channel}>{/** App components */}</Channel>
        ) : (
          <ChannelList onSelect={setChannel} />
        )}
      </Chat>
    </OverlayProvider>
  );
};
```

### Message List

`Channel` alone renders no UI. Add `MessageList` to render messages. It has no required props and works out of the box with `Channel`.

For `FlatList` props or threads, see the [MessageList component docs.](/chat/docs/sdk/react-native/v8/ui-components/message-list/)

![MessageList](@chat-sdk/react-native/v8/_assets/basics/hello-stream-chat/message_list.png)

```tsx {15}
import React, { useState } from "react";
import { Channel as ChannelType, StreamChat } from "stream-chat";
import { ChannelList, Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => {
  const [channel, setChannel] = useState<ChannelType>();

  return (
    <OverlayProvider>
      <Chat client={client}>
        {channel ? (
          <Channel channel={channel}>
            <MessageList />
          </Channel>
        ) : (
          <ChannelList onSelect={setChannel} />
        )}
      </Chat>
    </OverlayProvider>
  );
};
```

### Message Input

To send messages, render `MessageInput` below `MessageList`. It has no required props.

Set `keyboardVerticalOffset` on `Channel` based on your header height (0 in this example).

For customization or thread usage, see the [MessageInput component docs.](/chat/docs/sdk/react-native/v8/ui-components/message-input/)

![MessageInput](@chat-sdk/react-native/v8/_assets/basics/hello-stream-chat/message_input.png)

```tsx {15-16}
import React, { useState } from "react";
import { Channel as ChannelType, StreamChat } from "stream-chat";
import { ChannelList, Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => {
  const [channel, setChannel] = useState<ChannelType>();

  return (
    <OverlayProvider>
      <Chat client={client}>
        {channel ? (
          <Channel channel={channel} keyboardVerticalOffset={0}>
            <MessageList />
            <MessageInput />
          </Channel>
        ) : (
          <ChannelList onSelect={setChannel} />
        )}
      </Chat>
    </OverlayProvider>
  );
};
```

This renders a channel list and swaps in a functional message list + input when a channel is selected.

### Thread

`Thread` renders threaded replies. On mobile, threads typically live on a separate screen, so you must handle selection and navigation. Use `onThreadSelect` on `MessageList` to set a `thread` state and render `Thread`. This example has no back navigation (pseudo-navigation only).

For `onThreadDismount` and other details, see the [Thread component docs.](/chat/docs/sdk/react-native/v8/ui-components/thread/)

![Thread](@chat-sdk/react-native/v8/_assets/basics/hello-stream-chat/thread.png)

```tsx {9,15-24}
import React, { useState } from "react";
import { Channel as ChannelType, LocalMessage, StreamChat } from "stream-chat";
import { ChannelList, Chat, OverlayProvider } from "stream-chat-react-native";

const client = StreamChat.getInstance("api_key");

export const App = () => {
  const [channel, setChannel] = useState<ChannelType>();
  const [thread, setThread] = useState<LocalMessage | null>();

  return (
    <OverlayProvider>
      <Chat client={client}>
        {channel ? (
          <Channel
            channel={channel}
            keyboardVerticalOffset={0}
            thread={thread}
            threadList={!!thread}
          >
            {thread ? (
              <Thread />
            ) : (
              <>
                <MessageList onThreadSelect={setThread} />
                <MessageInput />
              </>
            )}
          </Channel>
        ) : (
          <ChannelList onSelect={setChannel} />
        )}
      </Chat>
    </OverlayProvider>
  );
};
```

## Summary

- Create a `StreamChat` client, connect a user, and pass the client to `Chat` (inside `OverlayProvider`).
- Render `ChannelList`, then use the selected `Channel` to render `MessageList` + `MessageInput`.
- Use `Thread` and `onThreadSelect` for threaded replies.


---

This page was last updated at 2026-04-17T17:33:44.446Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v8/ui-components/overview/](https://getstream.io/chat/docs/sdk/react-native/v8/ui-components/overview/).