# Custom Channel Background

## Basic Custom Background

For a static background, wrap `MessageList` and `MessageInput` with `ImageBackground`.

## Best Practices

- Keep message list backgrounds transparent so wallpapers show through without overlay artifacts.
- Ensure text contrast remains accessible against bright or busy images.
- Cache or preload background images to avoid layout jank when entering a channel.
- Persist per-channel settings so returning users see consistent visuals.
- Provide a safe default background when user-selected assets fail to load.

![167857632 C0bc9d67 0a84 4cf5 9d75 305e3bcd1f3d](https://user-images.githubusercontent.com/25864161/167857632-c0bc9d67-0a84-4cf5-9d75-305e3bcd1f3d.png)

Also adjust the `theme` so the list background is transparent.

```tsx
import {
  Channel,
  MessageInput,
  MessageList,
  ThemeProvider,
} from "stream-chat-react-native";
import { ImageBackground } from "react-native";

export const theme = {
  messageList: {
    container: {
      backgroundColor: "transparent",
    },
  },
};

const IMAGE_URI =
  "https://images.unsplash.com/photo-1549125764-91425ca48850?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NjF8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60";

const ChannelScreen = ({ channel }) => (
  <ThemeProvider style={theme}>
    <Channel channel={channel}>
      <ImageBackground
        style={{ flex: 1 }}
        source={{
          uri: IMAGE_URI,
        }}
      >
        <MessageList />
        <MessageInput />
      </ImageBackground>
    </Channel>
  </ThemeProvider>
);
```

---

## Custom Background With Selection Screen

Next, add a button that opens a screen where the user selects a background image per channel.

<table>
  <tr>
  <td align='center' width='50%'>

![168006715 Acae4b85 00cb 4b45 A127 Aa8f94c13895](https://user-images.githubusercontent.com/25864161/168006715-acae4b85-00cb-4b45-a127-aa8f94c13895.png)

  </td>
  <td align='center' width='50%'>

![168006193 8fd4ad85 7553 4956 A7c6 5d6979e15ee4](https://user-images.githubusercontent.com/25864161/168006193-8fd4ad85-7553-4956-a7c6-5d6979e15ee4.png)

  </td>
  </tr>
  <tr></tr>
  <tr>
  <td align='center'>
      <strong>Chat screen with customize background button</strong>
  </td>
  <td align='center'>
      <strong>Wallpaper overview screen with background image options</strong>
  </td>
  </tr>
</table>

### Store and manage channel preferences

To persist preferences (currently just the background URI), store data using `react-native-mmkv`, a key-value storage [framework](https://github.com/mrousavy/react-native-mmkv#mmkv). Follow the [installation steps](https://github.com/mrousavy/react-native-mmkv#installation) before continuing.

Start by creating `ChannelBackgroundView`. It renders the background by reading from the key-value store. Store an object to keep it extensible (for example, add dimming or background color later).

```tsx
import type { ViewProps } from "react-native";
import { useMMKVObject } from "react-native-mmkv";

type ChannelPreferences = {
  imageUri: string;
};

const DEFAULT_BACKGROUND_URI = "https://i.redd.it/3jfjc53fsyb61.jpg";

const ChannelBackgroundView = ({
  channelId,
  ...props
}: {
  channelId: string;
} & ViewProps) => {
  const [channelPreferences] = useMMKVObject<ChannelPreferences>(channelId);
  const uri = channelPreferences?.imageUri || DEFAULT_BACKGROUND_URI;

  return <ImageBackground {...props} source={{ uri }} />;
};
```

Use it in `ChannelScreen`: replace the static `ImageBackground` with `ChannelBackgroundView` and pass `channelId`.

```tsx
const ChannelScreen = ({ channel }) => {
  return (
    <ThemeProvider style={theme}>
      <Channel channel={channel}>
        <ChannelBackgroundView channelId={channel?.id} style={{ flex: 1 }}>
          <MessageList />
          <MessageInput />
        </ChannelBackgroundView>
      </Channel>
    </ThemeProvider>
  );
};
```

---

### Wallpaper overview screen

Add a screen where the user selects a wallpaper from a predefined list.

```tsx
import { StackNavigationProp } from "@react-navigation/stack";
import { RouteProp } from "@react-navigation/native";
import { useMMKVObject } from "react-native-mmkv";
import { View, SafeAreaView, Pressable, Image, StyleSheet } from "react-native";

const WallpaperOverviewScreen = ({
  navigation: { navigate },
  route: {
    params: { channelId },
  },
}: WallpaperOverviewScreenProps) => {
  const [_, setChannelPreferences] =
    useMMKVObject<ChannelPreferences>(channelId);
  return (
    <SafeAreaView
      style={{
        flex: 1,
        justifyContent: "center",
      }}
    >
      <View style={styles.container}>
        {BRIGHT_IMAGES?.map(({ imageUri = "" }, i) => {
          const handleOnPress = () => {
            setChannelPreferences({ imageUri });
            navigate("Channel");
          };
          return (
            <Pressable
              key={i}
              onPress={handleOnPress}
              style={{
                margin: 1,
                width: GRID_ITEM_WIDTH,
              }}
            >
              <Image style={styles.image} source={{ uri: imageUri }} />
            </Pressable>
          );
        })}
      </View>
    </SafeAreaView>
  );
};

type StackNavigatorParamList = {
  WallpaperOverviewScreen: {
    channelId: string;
  };
};

type WallpaperOverviewScreenProps = {
  navigation: StackNavigationProp<
    StackNavigatorParamList,
    "WallpaperOverviewScreen"
  >;
  route: RouteProp<StackNavigatorParamList, "WallpaperOverviewScreen">;
};

type ChannelPreferences = {
  imageUri: string;
};

const GRID_ITEM_WIDTH = "32.7%";

// Some random images that will get you started
const BRIGHT_IMAGES = [
  "https://images.unsplash.com/photo-1549125764-91425ca48850?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NjF8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1549241520-425e3dfc01cb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8ODB8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1554226321-24fdcddd5a55?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MjE5fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1550006490-9f0656b79e9d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8ODl8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1551506448-074afa034c05?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTEzfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553114835-6f7674d3c2c0?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTMyfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553075712-453f7213c24f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTMzfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1551917951-148edcd8ea8d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTU3fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553969923-bbf0cac2666b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MjA3fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553194642-29b272a173b9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTcwfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553356084-58ef4a67b2a7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTcxfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
  "https://images.unsplash.com/photo-1553526777-5ffa3b3248d8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8MTk4fHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=800&q=60",
].map((imageUri) => ({
  imageUri,
}));

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    flex: 1,
    alignContent: "stretch",
    flexWrap: "wrap",
    padding: 6,
  },
  image: {
    flex: 1,
    width: "100%",
  },
});
```

<admonition type="note">

Be aware of the fact that channel preferences were implemented with MMKV, a key-value storage framework.
There are alternative approaches to achieving the same goal, such as saving the channel preferences as [custom data](/chat/docs/javascript/channel_update/) on Stream's channel object.

</admonition>

### Add a configuration button

We will now add a button that will take the user from the Channel screen to our new WallpaperOverview screen.

```tsx
import { useNavigation } from "@react-navigation/native";
import {
  Channel,
  MessageInput,
  MessageList,
  ThemeProvider,
} from "stream-chat-react-native";
import { Pressable, Text, StyleSheet } from "react-native";

const ChannelScreen = ({ channel }) => {
  const { navigate } = useNavigation();
  const handleMenuOnPress = () =>
    navigate("WallpaperOverviewScreen", { channelId: channel?.id });

  return (
    <ThemeProvider style={theme}>
      <Channel channel={channel}>
        <ChannelBackgroundView channelId={channel?.id} style={{ flex: 1 }}>
          <Pressable style={styles.menuButton} onPress={handleMenuOnPress}>
            <Text>🎨</Text>
          </Pressable>
          <MessageList />
          <MessageInput />
        </ChannelBackgroundView>
      </Channel>
    </ThemeProvider>
  );
};

const styles = StyleSheet.create({
  menuButton: {
    position: "absolute",
    right: 0,
    top: 0,
    backgroundColor: "rgba(255,87,56,0.65)",
    borderRadius: 36,
    padding: 16,
    margin: 16,
    alignItems: "center",
    zIndex: 10,
  },
});

export const theme = {
  messageList: {
    container: {
      backgroundColor: "transparent",
    },
  },
};
```

---

### Optional: Connect all screens by navigation

If applicable to your use case, add our screens to a Navigation Stack by doing the following:

```tsx
import { createNativeStackNavigator } from "react-native-screens/native-stack";
import { NavigationContainer } from "@react-navigation/native";

const Stack = createNativeStackNavigator();

export default () => {
  return (
    <SafeAreaProvider>
      <ThemeProvider style={theme}>
        <NavigationContainer>
          <Stack.Navigator initialRouteName="Channel">
            <Stack.Screen
              component={ChannelScreen}
              name="Channel"
              options={noHeaderOptions}
            />
            <Stack.Screen
              component={WallpaperOverviewScreen}
              name="WallpaperOverviewScreen"
            />
          </Stack.Navigator>
        </NavigationContainer>
      </ThemeProvider>
    </SafeAreaProvider>
  );
};
```


---

This page was last updated at 2026-03-06T17:06:11.655Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/guides/channel-background-customization/](https://getstream.io/chat/docs/sdk/react-native/guides/channel-background-customization/).