# Configuration

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

The client builds most objects for you: channels, threads, composers, message lists, and its own managers for the connection, notifications and reminders. Each of them has settings, for example whether a composer saves drafts, how many messages a list loads per page, or how long the connection waits before it retries. This page goes through them in order: where settings come from, how your app sets them, how they combine, and how to read the result.

## Client and server settings

An object's settings come from two places:

- **Client settings** are what your app sets in code. They decide how the SDK's objects behave, for example how many messages a list loads per page, whether composers save drafts, or how a message is sent.
- **Server settings** live on the server, and your app only reads them. They say what the channel type allows, what the current user may do, whether slow mode is on and what the user's privacy settings are. See [Server settings](#server-settings).

Some features need both. Your app turns a feature on, and the server can still turn it off. [Who decides what](#who-decides-what) lists them. The next section is about client settings.

## Client settings

### The settings tree

The SDK's objects come in kinds: channels, threads, composers, message lists, and so on. Client settings are set per kind, and a setting applies to every object of its kind. They form a tree, with one key for each kind:

| Key                   | Configures                                                                                                                                                                                                                        |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel`             | every channel: typing, read and delivery events, replies, reminders, request handlers, its message list and its pinned-messages list                                                                                              |
| `thread`              | every thread: its request handlers and its reply list                                                                                                                                                                             |
| `messageComposer`     | every composer, in channels, threads and edits: text, attachments, drafts, link previews, polls, location, commands                                                                                                               |
| `messagePaginator`    | every message list, a channel's and a thread's alike. `channel.messagePaginator` and `thread.messagePaginator` override it field by field                                                                                         |
| `messageOperations`   | how every channel and thread sends, edits and deletes messages and reactions. Its settings say how many failed sends it keeps for retry, and for how long. `channel.messageOperations` and `thread.messageOperations` override it |
| `client`              | the client and its managers: the connection, connection recovery, delivery receipts, notifications, reminders and the thread list                                                                                                 |
| `liveLocationManager` | live location sharing                                                                                                                                                                                                             |
| `searchController`    | search                                                                                                                                                                                                                            |

Every setting is listed in [Configuration tree](https://getstream.io/chat/docs/sdk/react-native/v10/reference/state/configuration-tree/).

The keys are fixed. You can't add one, not even with module augmentation. To make a class of your own configurable, see [Making your own class configurable](https://getstream.io/chat/docs/sdk/react-native/v10/reference/state/making-your-own-class-configurable/).

### When you create the client

Pass the tree when you create the client. Say every composer should save drafts, and every channel's message list should load 50 messages per page. With `useCreateChatClient`, pass the tree as `options`:

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

export const useChatClient = () =>
  useCreateChatClient({
    apiKey: "YOUR_API_KEY",
    options: {
      channel: { messagePaginator: { pageSize: 50 } },
      messageComposer: { drafts: { enabled: true } },
    },
    tokenOrProvider: "YOUR_USER_TOKEN",
    userData: { id: "YOUR_USER_ID" },
  });
```

The hook reads `options` once, on the first render, and passes them to the `StreamChat` constructor. A client you create yourself takes the same tree as its second argument:

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

const chatClient = new StreamChat("YOUR_API_KEY", {
  channel: { messagePaginator: { pageSize: 50 } },
  messageComposer: { drafts: { enabled: true } },
});
```

`StreamChat.getInstance(apiKey, config)` takes the same tree the first time it's called, and ignores it after that.

A few settings are read only while the client is being built, so they have to go here. [Settings read once](#settings-read-once) lists them.

### After the client is created

The client keeps the tree in `client.config`, and you can change it at any time. A change also reaches the objects that exist already, except for the few [settings read once](#settings-read-once). It can also undo what you changed on one object with `updateConfig()`, as [How the settings combine](#how-the-settings-combine) explains.

For example, to turn drafts off again:

```ts
client.config.set({
  messageComposer: { drafts: { enabled: false } },
});
```

`client.config` has these methods:

- `client.config.set(tree)` merges into what's already set, so a later call changes only the paths it names.
- `client.config.setConfig(key, subtree)` does the same for one key, such as `"messageComposer"`.
- `client.config.getConfig(key)` returns what you set for one key, and `client.config.getTree()` the whole tree.
- `client.config.reset(key?)` clears `client.config`, for one key or for every key when you pass none. That includes the tree you passed when you created the client, the settings the SDK's components registered (see [What the React Native SDK sets](#what-the-react-native-sdk-sets)), and the setup function, whose teardown runs. Every existing object then rebuilds its settings right away. It goes back to the SDK's defaults and drops its `updateConfig()` changes. It keeps the options your code passed when it created the object, such as a `ChannelPaginator`'s `paginatorOptions`, and the server's limits still apply. Only the [settings read once](#settings-read-once) have no effect on existing objects, because those objects already used them when they were created.

### Settings for one object

The ways above apply to every object of a kind. Two ways change a single object:

- **Its constructor**, for an object your code creates. A `ChannelPaginator`, for example, takes its settings when you create it, as [Channel list paginators](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/channel-list-paginators/) shows.
- **`updateConfig(patch)`**, which these objects have:

| Call                                                  | Changes                                     |
| ----------------------------------------------------- | ------------------------------------------- |
| `channel.messageComposer.updateConfig(patch)`         | the channel's composer                      |
| `channel.messagePaginator.updateConfig(patch)`        | the channel's message list                  |
| `channel.pinnedMessagesPaginator.updateConfig(patch)` | the channel's pinned-messages list          |
| `channel.messageOperations.updateConfig(patch)`       | how the channel sends messages              |
| `Thread.updateConfig(patch)`                          | the thread                                  |
| `thread.messageComposer.updateConfig(patch)`          | the thread's composer                       |
| `thread.messagePaginator.updateConfig(patch)`         | the thread's reply list                     |
| `thread.messageOperations.updateConfig(patch)`        | how the thread sends replies                |
| `channelPaginator.updateConfig(patch)`                | one channel paginator your code created     |
| `client.threads.paginator.updateConfig(patch)`        | the thread list                             |
| `client.reminders.paginator.updateConfig(patch)`      | the reminder list                           |
| `userGroupPaginator.updateConfig(patch)`              | one user group list your code created       |
| `client.threads.updateConfig(patch)`                  | the thread manager                          |
| `client.reminders.updateConfig(patch)`                | the reminder manager                        |
| `client.notifications.updateConfig(patch)`            | notifications                               |
| `client.messageDeliveryReporter.updateConfig(patch)`  | delivery receipts                           |
| `client.connectionRecovery.updateConfig(patch)`       | connection recovery                         |
| `client.wsConnection.updateConfig(patch)`             | the WebSocket connection                    |
| `client.networkConnection.updateConfig(patch)`        | the network status observer                 |
| `liveLocationManager.updateConfig(patch)`             | the live location manager your code created |
| `searchController.updateConfig(patch)`                | the search controller your code created     |

A channel has no `updateConfig()`, so a channel's own settings, such as typing and read events, can be set only for every channel at once, in `client.config`.

### Setup functions

Some customizations aren't values, such as adding composer middleware, or a setting that depends on the object. A setup function covers those. It's code that runs for every object of its kind, the existing ones and every one created later. It receives the object, and can change it, for example with `MessageComposer.updateConfig()`:

```ts
client.config.setSetupFunction("messageComposer", ({ composer }) => {
  // Composers in threads don't send typing events. Channel composers keep the default.
  if (!composer.threadId) return;
  composer.updateConfig({ text: { publishTypingEvents: false } });
});
```

- There is one setup function per kind: `channel`, `thread`, `messageComposer`, `client`, `liveLocationManager` and `searchController`. Setting another one replaces it, and `null` removes it.
- It runs again whenever the settings of its kind change, so it can run more than once for the same object.
- An error it throws is logged and doesn't break the object.
- For a composer or a thread, it runs while the object's subscriptions are registered. `Channel` and `Thread` register them for the composer they show, and `Chat` registers them for the threads the client keeps.

#### Teardown functions

A setup function can return a teardown function, which undoes what the setup function did. The teardown runs:

- before the setup function runs again for the same object;
- when you replace or remove the setup function, including with `client.config.reset()`;
- when the object is no longer used, for example a channel when it's disposed, a composer when the last `Channel` or `Thread` showing it unmounts, and a thread when its subscriptions end.

Return one when running the setup function twice would do something twice. For example, a setup function that listens to an event adds a new listener on every run, so it returns the function that removes it:

```ts
client.config.setSetupFunction("channel", ({ channel }) => {
  const { unsubscribe } = channel.on("message.new", (event) => {
    console.log(`New message in ${channel.cid}`, event.message?.id);
  });
  return unsubscribe;
});
```

Middleware inserted without `unique: true` is the same. Every run adds another copy, and the teardown removes it by its id:

```ts
import type { StreamChat } from "stream-chat";
import { voteActionsMiddleware } from "./voteActionsMiddleware";

export const setUpComposers = (client: StreamChat) =>
  client.config.setSetupFunction("messageComposer", ({ composer }) => {
    composer.compositionMiddlewareExecutor.insert({
      middleware: [voteActionsMiddleware],
      position: { after: "stream-io/message-composer-middleware/attachments" },
    });
    return () =>
      composer.compositionMiddlewareExecutor.remove(voteActionsMiddleware.id);
  });
```

A setup function that gives the same result however often it runs doesn't need one. Calling `updateConfig()` is like that, and so is inserting middleware with `unique: true`, which replaces the copy an earlier run inserted:

```ts
import type { StreamChat } from "stream-chat";
import { voteActionsMiddleware } from "./voteActionsMiddleware";

export const setUpComposers = (client: StreamChat) =>
  client.config.setSetupFunction("messageComposer", ({ composer }) => {
    composer.compositionMiddlewareExecutor.insert({
      middleware: [voteActionsMiddleware],
      position: { after: "stream-io/message-composer-middleware/attachments" },
      unique: true,
    });
  });
```

Without a teardown, that middleware stays on the existing composers when you replace or remove the setup function, or call `client.config.reset()`. If it should go then, return a teardown that calls `composer.compositionMiddlewareExecutor.remove(voteActionsMiddleware.id)`.

[Composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/) shows a setup function that adds middleware, with its teardown.

### How the settings combine

Each object puts its settings together from everything above, in this order. A later step wins for the fields it sets:

1. The SDK's defaults.
2. Your settings in `client.config`.
3. Settings passed to the constructor of an object your code creates.
4. The setup function.
5. `updateConfig()` calls, including the ones a setup function makes.
6. The server's limits, last, so nothing above can go beyond them.

The object puts them together again whenever one of these changes. That happens when your code calls `client.config.set()`, `client.config.setConfig()`, `client.config.reset()` or `client.config.setSetupFunction()`, and, for channels and composers, when the channel's settings arrive from the server.

So a setting you change on one object with `updateConfig()` can be undone. Composers keep these changes, except after `client.config.reset()`. Other objects don't. A channel, for example, rebuilds the settings of its message list, its pinned-messages list and its message operations whenever the `channel`, `messagePaginator` or `messageOperations` key changes. For a change that must last, use a setup function, which runs again every time the settings are put together.

### What the React Native SDK sets

The SDK's components register a few settings of their own:

- **The network status reporter.** `Chat` sets `client.networkConnection.statusReporter` to `netInfoStatusReporter` when it mounts, unless a reporter is registered there already. It reports the device's network through `@react-native-community/netinfo`. `useCreateChatClient` also passes it to the constructor, so the client has it from the start. A client you create yourself gets it only when `Chat` mounts, unless you pass `netInfoStatusReporter` from `stream-chat-react-native` in the tree you give the constructor. See [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/).
- **`client.persistUserOnConnectionFailure`.** `Chat` sets it to the value of its `enableOfflineSupport` prop, `false` by default, when it mounts and whenever the prop changes. It overwrites a value you set before. See [Offline support](https://getstream.io/chat/docs/sdk/react-native/v10/state/offline-support/offline-support/).
- **`messageComposer.attachments.pendingUploadsEnabled`.** With `enableOfflineSupport`, `Chat` sets it to `true`, unless you set it yourself. It never sets it back. See [Sending with pending uploads](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/sending-with-pending-uploads/).
- **The message list's page size.** `Channel` sets the page size of its channel's message list to 25 when it mounts, unless a `pageSize` is set under `messagePaginator` or `channel.messagePaginator` in `client.config`. It sets it on the list itself, so the channel drops it when the list's settings are rebuilt, as described above. The list then loads 100 messages a page until the channel opens again. Set the page size in `client.config` to keep it.

### Settings read once

A few settings are read only when an object is created. Setting them later doesn't change the objects that exist already, and the client logs a warning:

- `axiosRequestConfig`, `baseURL` and `notificationManager` under the `client` key. Pass them in the tree you create the client with.
- `initialCursor`, `initialOffset` and `unreadReferencePolicy` of a message list, and `initialCursor` and `initialOffset` of the pinned-messages list. Set them before the channels and threads are created, in the tree you create the client with.

Two composer settings are also read only when a composer is built, but without a warning: `linkPreviews.debounceURLEnrichmentMs` and `location.getDeviceId`. Set them before the composers are created.

## Server settings

These live on the server. The client receives them with the channel or with the user.

- **Channel type settings**, set in the [Stream Dashboard](https://getstream.io/signin/?product=chat) or through the server API: `typing_events`, `read_events`, `replies`, `user_message_reminders`, `delivery_events`, `uploads`, `url_enrichment`, `shared_locations`, `polls`, `max_message_length` and `commands`. A channel's own `config_overrides` can narrow them for that channel alone. They arrive with the channel and are in `channel.serverConfig`, which is `undefined` until the channel has been queried or watched.
- **Capabilities** say what the current user may do in each channel, from their roles and the channel type's permissions. They're in `channel.state` as `ownCapabilities`, a list such as `"send-message"`, `"upload-file"` and `"send-reaction"`. When they change, `channel.state` updates. After a channel query or `Channel.updatePartial()` returns different capabilities, the client also raises [`capabilities.changed`](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/events/#events-the-client-raises-itself). The server doesn't send it.
- **Per-channel settings** are stored with the channel. Slow mode is one: `channel.data.cooldown` holds the seconds between two messages. A user with the `"skip-slow-mode"` capability isn't held back.
- **The user's privacy settings** for typing indicators and delivery receipts are in `client.user.privacy_settings`. When the user turns one off, the client doesn't send those events for them.

## Who decides what

Some settings belong to the client alone, some to the server alone, and some need both sides to allow them.

**Only the client** decides:

- the page size, update throttling, memory cap and unread policy of message lists (`messagePaginator`);
- how messages are sent, through the request handlers (`channel.requestHandlers` and `thread.requestHandlers`), and how many failed sends are kept for retry, and for how long (`messageOperations`);
- the composer's features, which are drafts, sending before uploads finish, a custom CDN or upload function, the accepted files and their number, how links are found and how long to wait before fetching a preview, the location device id and the shortest sharing time, command validation, the default text, and `retainCompositionOnSubmit`;
- timing for the connection, recovery, notifications and reminders;
- search.

**Only the server** decides capabilities, slow mode, the user's privacy settings, and the list of commands a channel offers, which is in `channel.config.availableCommands`.

**Both sides must allow it.** The server can only narrow what the client turns on, so a feature is on when both say yes:

| Client setting                                               | Server setting                                      |
| ------------------------------------------------------------ | --------------------------------------------------- |
| `channel.typingEvents.enabled`                               | `typing_events`                                     |
| `channel.readEvents.enabled`                                 | `read_events`                                       |
| `channel.replies.enabled`                                    | `replies`                                           |
| `channel.userMessageReminders.enabled`                       | `user_message_reminders`                            |
| `channel.deliveryEvents.enabled`                             | `delivery_events`                                   |
| `messageComposer.attachments.enabled`                        | `uploads`, unless `attachments.customCdn` is `true` |
| `messageComposer.linkPreviews.enabled`                       | `url_enrichment`                                    |
| `messageComposer.location.enabled`                           | `shared_locations`                                  |
| `messageComposer.polls.enabled`                              | `polls`                                             |
| `messageComposer.text.maxLengthOnSend` and `maxLengthOnEdit` | `max_message_length`, which caps both               |

The value in effect is on each object. See [Checking whether a feature is on](#checking-whether-a-feature-is-on).

## Checking whether a feature is on

Every configurable object, the channel, the thread, the composer, the paginators and the client's managers, has the same members for its settings:

- `config` holds the settings in effect, the server's limits included. Read it.
- `configState` is the store that holds them. Subscribe to it to follow changes, for example when a channel's server settings arrive with its first query.
- `updateConfig(patch)` changes this object only, see [Settings for one object](#settings-for-one-object). A channel doesn't have it.

The managers inside a composer, such as `attachmentManager` and `linkPreviewsManager`, read their part of the composer's settings, so follow them through the composer's `configState`.

Don't check `client.config` or `channel.serverConfig` for this. `client.config` holds only what your app set, and `serverConfig` only what the server allows. Only the object's `config` combines both.

For example, to know whether uploads are allowed now and whenever that changes:

```ts
const composer = channel.messageComposer;

// Now
const canAttach = composer.config.attachments.enabled;

// From now on, whenever it changes
const unsubscribe = composer.configState.subscribeWithSelector(
  ({ attachments }) => ({ enabled: attachments.enabled }),
  ({ enabled }) => {
    console.log(enabled ? "Uploads allowed" : "Uploads not allowed");
  },
);
```

A component that shows or hides an attach button reads the same value with `useStateStore(composer.configState, selector)`. See [Reading state in components](https://getstream.io/chat/docs/sdk/react-native/v10/ui/reading-state-in-components/).

Some features also need a capability. To decide whether to offer polls, check the composer's setting and the user's capability:

```ts
const canCreatePolls =
  channel.messageComposer.config.polls.enabled &&
  channel.state.getLatestValue().ownCapabilities.includes("send-poll");
```

## Used in

- [Sending with pending uploads](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/sending-with-pending-uploads/)
- [Drafts](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/drafts/)
- [Link previews](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/link-previews/)
- [Polls](https://getstream.io/chat/docs/sdk/react-native/v10/features/rich-content/polls/)
- [Typing indicators](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/typing-indicators/)
- [Read state, unread and receipts](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/read-state-unread-and-receipts/)
- [Message operations](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/message-operations/), for request handlers
- [Composer middleware](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/composer-middleware/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/configuration/](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/configuration/).