# How the SDK is built

The SDK has two layers. This page follows one action through both of them: a user opens a channel and sends a message. It ends with the rule that follows from it: which layer to change for what.

## The two layers

- **State** lives in `stream-chat`. Channels, threads, channel lists, message lists and message composers are objects, and each keeps its data in stores that anything can subscribe to. You configure these objects through `client.config`. None of this depends on React.
- **UI** lives in `stream-chat-react-native`, or `stream-chat-expo` in Expo apps. Its components subscribe to those stores with `useStateStore`, render what they hold, and call methods on the objects when the user acts. You replace any component through `WithComponents`.

Where a channel or a thread opens is up to your app's navigation. The SDK calls back when the user picks a channel (`onSelect` on `ChannelList`) or opens a thread (`onThreadSelect` on `MessageList` and `ThreadList`), and your screens render `Channel` and `Thread`.

## One action through the layers

The user has the app from the [Quick start](https://getstream.io/chat/docs/sdk/react-native/getting-started/quick-start/#add-a-channel-list-with-navigation) open: a channel list screen and a channel screen. They open "Design" and send "Hello". Step through what happens in each layer:

```mermaid
flowchart LR
  subgraph UI["UI: stream-chat-react-native"]
    CL[ChannelList]
    ML[MessageList]
    MC[MessageComposer]
  end
  subgraph State["State: stream-chat"]
    CP[ChannelPaginator]
    MP[channel.messagePaginator]
    CO[channel.messageComposer]
    MO[channel.messageOperations]
  end
  API[(Stream API)]
  CP -- channels --> CL
  MP -- messages --> ML
  CO -- what is typed --> MC
  MC -- "send()" --> CO
  CO --> MO
  MO -- optimistic message --> MP
  MO -- request --> API
  API -- "response, message.new" --> MP
  API -- "message.new" --> CP
```

**1. Load**

`ChannelPaginator` loads the channels from the Stream API with a channel query, and feeds them to `ChannelList`: the list subscribes to `paginator.state` and renders a row for each channel. The paginator is registered on `client.channelManager`, which keeps it up to date as events arrive.

**2. Open**

Tapping "Design" calls the list's `onSelect` with the channel, and the app navigates to its channel screen, which renders `Channel` for it. `Channel` watches the channel unless it's already watched and loaded with its latest messages (or has no id yet), activates it, and marks it read if it has unread messages. Inside it, `MessageList` subscribes to `channel.messagePaginator` and renders its messages, and `MessageComposer` renders from `channel.messageComposer`.

**3. Type**

Typing in `MessageComposer` writes the text into `channel.messageComposer`, and the composer UI renders the composer's state back.

**4. Send**

Pressing the send button calls `MessageComposer.send()`, which hands the message to `channel.messageOperations`. Two paths follow. The local one adds the message to `messagePaginator` at once, marked as sending, so `MessageList` shows it right away. The other sends the request to the Stream API, through `channel.requestHandlers.sendMessageRequest` in `client.config` if you set one. The composer is cleared.

**5. Response**

The API answers with its copy of the message. `messageOperations` stores it in `messagePaginator` in place of the local one, marked as received, and `MessageList` re-renders it as sent. If the request fails, the message is marked failed instead, and the user can retry it from the message menu. With [offline support](https://getstream.io/chat/docs/sdk/react-native/state/offline-support/offline-support/), a text message sent while the device is offline is kept in the local database and sent again when the connection comes back.

**6. Event**

The API also sends `message.new` to every client watching the channel, this one included. The channel ingests the message into `messagePaginator`, where it's already present, so nothing changes there, and your own message doesn't count as unread. The channel manager re-inserts "Design" into its paginators, which sort by last message, so it moves to the top and `ChannelList` re-renders in that order.

The UI rendered things and the app's navigation placed them; the state layer loaded the data, sent the message and updated the stores. In a thread, the same steps run on `thread.messagePaginator`, `thread.messageComposer` and `thread.messageOperations`, and the send still goes through the channel's request handler.

## The rule

**Change behavior in the state layer. Change appearance in the UI layer.**

- **Behavior** is what the objects do: what they load, what they send and what they allow. Change it through `client.config`: configuration values, request handlers, and setup functions that add composer middleware. The change applies wherever the object is used: in the SDK's components, in your own components, and in channels and threads alike.
- **Appearance** is what a component renders. Replace the component through `WithComponents`, or change the theme.
- **Placement** is where a channel or a thread opens. It belongs to your app's navigation: the screens that render `Channel` and `Thread`, and the callbacks that open them.

Two examples:

- **Turning on drafts** is behavior: set `messageComposer.drafts.enabled` in `client.config`. It reaches every message composer, in channels and threads, including a composer UI of your own, because it configures the composer objects, not a component.
- **Changing the date separator** is appearance: pass an `InlineDateSeparator` override to `WithComponents`. Every message list below it renders yours.

## Where each layer is taught

- **State.** The State section explains how the objects hold data, load it, keep it current and change it. Start with [Reactive state](https://getstream.io/chat/docs/sdk/react-native/state/state-and-entities/reactive-state/). [Channel list paginators](https://getstream.io/chat/docs/sdk/react-native/state/loading-data/channel-list-paginators/) cover channel lists, and [Message composer](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/message-composer/) and [Message operations](https://getstream.io/chat/docs/sdk/react-native/state/writing-messages/message-operations/) cover writing and sending. [Configuration](https://getstream.io/chat/docs/sdk/react-native/state/state-and-entities/configuration/) covers `client.config`, and [Offline support](https://getstream.io/chat/docs/sdk/react-native/state/offline-support/offline-support/) covers the local database.
- **UI.** The UI section explains [how components get their state](https://getstream.io/chat/docs/sdk/react-native/ui/providers-and-context-hooks/) and how to change any of them, for example through [Theming](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/theming/), [Localization](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/localization/) and [Actions](https://getstream.io/chat/docs/sdk/react-native/ui/components/actions/). The props of every component are in the Component reference, starting with [Chat](https://getstream.io/chat/docs/sdk/react-native/component-reference/chat/chat/).
- **Layout.** The UI section's Layout pages, starting with [Navigation](https://getstream.io/chat/docs/sdk/react-native/ui/layout/navigation/), explain where channels and threads open and how users move between them.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/getting-started/how-the-sdk-is-built/](https://getstream.io/chat/docs/sdk/react-native/getting-started/how-the-sdk-is-built/).