# Theming

> **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 SDK's look comes from a theme: a JavaScript object with the design tokens the components color themselves with, and a style object for each component. You pass a partial theme, and the SDK merges it over its default. Setting one token changes everything that reads it: `chatBgOutgoing` alone recolors the bubbles of the user's own messages in every list. Replace a component through [`WithComponents`](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/overriding-components/) when you need a different structure; for colors, spacing, sizes and fonts, change the theme.

## Setting the theme

`OverlayProvider` takes the theme as `value.style`, and `Chat` as `style`. Pass the same object to both:

```tsx
import type { PropsWithChildren } from "react";
import type { StreamChat } from "stream-chat";
import { Chat, OverlayProvider } from "stream-chat-react-native";
import type { DeepPartial, Theme } from "stream-chat-react-native";

// Declared once, at module scope.
const theme: DeepPartial<Theme> = {
  channelPreview: {
    title: { fontSize: 17 },
  },
  semantics: {
    accentPrimary: "#0d47a1",
    buttonPrimaryBg: "#0d47a1",
    chatBgOutgoing: "#1e3a8a",
    chatTextOutgoing: "#ffffff",
    textLink: "#1e40af",
  },
};

export const App = ({
  children,
  client,
}: PropsWithChildren<{ client: StreamChat }>) => (
  <OverlayProvider value={{ style: theme }}>
    <Chat client={client} style={theme}>
      {children}
    </Chat>
  </OverlayProvider>
);
```

1. `OverlayProvider` merges the theme over the default, and gives it to everything below it, the message menu's background and the image gallery included.
2. `Chat` does the same for everything below it. Without its own `style`, `Chat` takes `OverlayProvider`'s theme. With one, it merges that `style` over the default, not over `OverlayProvider`'s, which is why the two get the same object.
3. Every component reads the merged theme with `useTheme()`.

`DeepPartial<Theme>` makes every key optional, at every depth, so you set only what you change. The merge is deep: `channelPreview.title.fontSize` keeps every other style of the title.

The theme is merged again when the object changes, so declare it at module scope, or build it with `useMemo`, as below for dark mode. The full shape of `Theme` is on [Theme object](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/theme-object/), and its defaults are in `package/src/contexts/themeContext/utils/theme.ts` in the SDK's repository.

## Design tokens

The tokens come in tiers, and each tier is built from the one above it:

1. **Foundations.** Raw values: colors such as `blue500` and `slate100`, and the scales of sizes, spacing, radii and type.
2. **Primitives.** Values for a purpose that doesn't change with the color scheme: spacing (`spacingMd`), radii (`radiusLg`), type sizes and weights (`typographyFontSizeMd`), and shadows (`lightElevation1`).
3. **Semantic tokens.** What a color is for: `accentPrimary`, `textPrimary`, `backgroundCoreApp`, `borderCoreDefault`, and the colors of one kind of component, such as `chatBgOutgoing` for the bubbles of the user's own messages, `avatarBgDefault` and `buttonPrimaryBg`. There is a light set and a dark set.
4. **Component values.** Sizes for one part of one component, such as the corner radius of a message bubble or the padding of a button.

The SDK generates the tokens from its design files, with values for each platform: the type scale on iOS and on Android follows each platform's own.

Only the semantic tokens are part of the theme, under `semantics`. The other tiers are constants the components use directly, so you change what they control through the per-component styles below.

### Changing colors

Set semantic tokens under `semantics`, as the first example does. The SDK resolves each token from the tier above it, `accentPrimary` from `brand500` for example, **before** it merges your theme. So a token you set changes exactly the components that read that token, and nothing built from it:

- Setting `accentPrimary` recolors what reads `accentPrimary`: the read receipts, the selection circles, the loading spinners and upload progress, and poll options. The primary buttons read `buttonPrimaryBg` and the badges `badgeBgPrimary`, both already resolved from the default accent, so set those too, as the example does for the buttons.
- Setting `brand500` doesn't recolor the accent, the links or the bubbles, because they were resolved from it before your theme was merged. Almost no component reads a brand step directly (the member role badge in channel details reads `brand150` and `brand900`), so to recolor the UI, set the semantic tokens themselves.
- A value is used as you write it. A `$`-reference such as `"$brand500"` isn't resolved in your theme.

To find the tokens one part uses, look in its component's source for `semantics.`. The tokens and their default values are listed on [Design tokens](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/design-tokens/).

### Changing one component

Most components have a key in the theme, named after them, with a style for each of their parts: `channelPreview.title`, `messageComposer.inputBoxContainer`, `inlineDateSeparator.container`. A component spreads the style over its own, so a property you set wins over the default, and the properties you leave out keep it.

```ts
import type { DeepPartial, Theme } from "stream-chat-react-native";

export const theme: DeepPartial<Theme> = {
  channelPreview: {
    title: { color: "#bf360c", fontWeight: "700" },
  },
  inlineDateSeparator: {
    container: { borderRadius: 4 },
  },
  messageItemView: {
    content: {
      timestampText: { fontSize: 11 },
    },
  },
};
```

Most values are React Native styles: `ViewStyle`, `TextStyle` or `ImageStyle`. Some are not: icon entries take `IconProps`, such as `channelPreview.mutedStatus`; `messageItemView.content.markdown` takes the styles of the Markdown renderer, one `TextStyle` or `ViewStyle` for each Markdown element; and a few are plain numbers, such as `screenPadding`. [Markdown styles](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/theme-object/#markdown-styles) lists the Markdown elements and what each key styles.

The top-level keys are: `aiTypingIndicatorView`, `attachmentPicker`, `attachmentSelectionBar`, `audioAttachment`, `avatar`, `avatarStack`, `bottomSheetModal`, `channel`, `channelDetails`, `channelDetailsMenu`, `channelListFooterLoadingIndicator`, `channelListHeaderErrorIndicator`, `channelListLoadingIndicator`, `channelListSkeleton`, `channelListView`, `channelPreview`, `dateHeader`, `emptyList`, `emptySearchResult`, `emptyStateIndicator`, `fileAttachmentListSkeleton`, `groupAvatar`, `iconBadge`, `iconSquare`, `imageGallery`, `inlineDateSeparator`, `loadingDots`, `loadingErrorIndicator`, `loadingIndicator`, `memberListSkeleton`, `messageComposer`, `messageItemView`, `messageList`, `messageMenu`, `messagePreview`, `notification`, `notificationList`, `pinnedMessageListSkeleton`, `poll`, `progressControl`, `reply`, `screenPadding`, `selectionCircle`, `semantics`, `spinner`, `thread`, `threadListItem`, `threadListSkeleton`, `threadListUnreadBanner`, `typingIndicator`, `userListSkeleton` and `waveProgressBar`. Each one's parts are on [Theme object](https://getstream.io/chat/docs/sdk/react-native/v10/reference/theme/theme-object/).

### Fonts

The SDK's components set no font family, apart from a monospace font for Markdown code blocks, so their text renders in the platform's system font. To use your app's font, set `fontFamily` on the text styles you want it in, such as `channelPreview.title` and `messageItemView.content.markdown.text`. The font has to be loaded by your app first.

## Light and dark

The theme follows the device's color scheme. `useColorScheme()` decides which set of semantic tokens the SDK starts from, and the UI switches when the user changes the setting.

Your `semantics` apply to whichever set is active. To use different values in each scheme, pick the theme by the scheme:

```tsx
import { useColorScheme } from "react-native";
import type { PropsWithChildren } from "react";
import type { StreamChat } from "stream-chat";
import { Chat, OverlayProvider } from "stream-chat-react-native";
import type { DeepPartial, Theme } from "stream-chat-react-native";

const lightTheme: DeepPartial<Theme> = {
  semantics: {
    accentPrimary: "#0d47a1",
    buttonPrimaryBg: "#0d47a1",
    chatBgOutgoing: "#dbeafe",
  },
};

const darkTheme: DeepPartial<Theme> = {
  semantics: {
    accentPrimary: "#4a90e2",
    buttonPrimaryBg: "#4a90e2",
    chatBgOutgoing: "#1e3a8a",
  },
};

export const App = ({
  children,
  client,
}: PropsWithChildren<{ client: StreamChat }>) => {
  const scheme = useColorScheme();
  const theme = scheme === "dark" ? darkTheme : lightTheme;

  return (
    <OverlayProvider value={{ style: theme }}>
      <Chat client={client} style={theme}>
        {children}
      </Chat>
    </OverlayProvider>
  );
};
```

Both themes are module-scope objects, so the theme changes only when the scheme does. Component styles you set apply in both schemes, so set colors through `semantics` rather than in component styles, or give each scheme its own component styles too.

The SDK has no prop that fixes the scheme. To keep the app light whatever the device's setting, call React Native's `Appearance.setColorScheme("light")`, which `useColorScheme()` follows.

## Reading the theme in your own components

`useTheme()` returns `{ theme }`, the merged theme of the nearest provider. A component that colors itself from it follows your theme and the color scheme like the SDK's own:

```tsx
import { StyleSheet, Text, View } from "react-native";
import { useTheme } from "stream-chat-react-native";

export const NewBadge = () => {
  const {
    theme: { semantics },
  } = useTheme();

  return (
    <View style={[styles.badge, { backgroundColor: semantics.badgeBgPrimary }]}>
      <Text style={{ color: semantics.badgeTextOnAccent }}>New</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  badge: { borderRadius: 8, paddingHorizontal: 6 },
});
```

`useTheme()` throws outside `OverlayProvider` and `Chat`. Read the theme each render rather than caching it in a ref or in module state, so the component updates when the theme or the scheme changes.

## The user's own messages

`myMessageTheme` on `Channel` and `Thread` is a second partial theme, merged over the app's theme for the current user's messages only, and their date separators:

```tsx
import type { Channel as StreamChannel } from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
} from "stream-chat-react-native";
import type { DeepPartial, Theme } from "stream-chat-react-native";

const myMessageTheme: DeepPartial<Theme> = {
  messageItemView: {
    content: {
      timestampText: { fontStyle: "italic" },
    },
  },
};

export const ChannelScreen = ({ channel }: { channel: StreamChannel }) => (
  <Channel channel={channel} myMessageTheme={myMessageTheme}>
    <MessageList />
    <MessageComposer />
  </Channel>
);
```

The bubble colors of the user's own messages already have their own tokens, `chatBgOutgoing` and `chatTextOutgoing`, so use `myMessageTheme` for what has no such token. The message list merges it once, and again only when its content changes. `forceAlignMessages` on `Channel` puts every message on one side instead.

## Right-to-left layouts

The SDK follows React Native's layout direction. When `I18nManager.isRTL` is `true`, the layout mirrors, and so do the icons that point in a direction, such as the reply and send arrows. `I18nManager.forceRTL(true)` takes effect after the app restarts.

## Used in

- [Quick start](https://getstream.io/chat/docs/sdk/react-native/v10/getting-started/quick-start/), whose `OverlayProvider` and `Chat` take the theme.
- [UI Cookbooks](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/overview/): when the theme isn't enough, a UI cookbook replaces the component, for example [Incoming and outgoing styles](https://getstream.io/chat/docs/sdk/react-native/v10/ui-cookbook/message/incoming-and-outgoing-styles/).

## Pitfalls

- **A theme on `Chat` only, or on `OverlayProvider` only.** `Chat` with its own `style` doesn't build on `OverlayProvider`'s, and `OverlayProvider` never sees `Chat`'s. Pass the same object to both.
- **Setting a token and expecting the tokens built from it to follow.** Tokens are resolved before your theme is merged. Set every token the parts you want to change read: `accentPrimary` and `buttonPrimaryBg`, for example.
- **A `$`-reference in your theme.** It isn't resolved, and renders as an invalid color. Write the value.
- **A theme object created on every render.** Each new object merges the whole theme again. Declare it at module scope, or memoize it.
- **Colors in component styles with dark mode.** A color you set in a component style shows in both schemes. Use `semantics`, or pick the theme by the scheme.
- **`useTheme()` outside the providers.** It throws. Render your component below `OverlayProvider` or `Chat`.

---

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