# Custom Icons

Every icon the SDK renders — the send arrow, delivery checkmarks, the mute and pin badges, the attachment-picker glyphs, and more — can be replaced with your own. Icons are exposed through the same [`WithComponents`](/chat/docs/sdk/react-native/customization/custom-components/) provider used for other UI components, grouped under a single `icons` key.

## Best Practices

- Start with [theming](/chat/docs/sdk/react-native/customization/theming/); most icon color and size tweaks can be done there without a custom icon.
- Override icons with `WithComponents` under the `icons` key — you only need to provide the icons you want to change.
- Have custom icons accept `IconProps` and honor the `height`/`width`/`size` and color (`fill`/`stroke`/`pathFill`) values the SDK passes, so they match sizing and theming everywhere they render.
- Set app-wide icon overrides high in the tree (around `Chat`), and override more specifically deeper down only when needed.
- Keep custom icons lightweight — the same icon may render once per message row inside `MessageList`.

## Overriding an icon

Pass an `icons` object to the `overrides` prop of `WithComponents`. Each key is the name of an SDK icon, and the value is your replacement component. Only the icons you list are replaced — every other icon keeps its default.

```tsx
import Svg, { Path } from "react-native-svg";
import {
  WithComponents,
  Channel,
  MessageList,
  MessageInput,
  IconProps,
} from "stream-chat-react-native";

// A custom mute icon. It receives IconProps, so honor the size and color
// the SDK passes to keep it consistent with the surrounding UI.
const CustomMuteIcon = ({
  height = 24,
  width = 24,
  fill = "#000",
  ...rest
}: IconProps) => (
  <Svg height={height} width={width} viewBox="0 0 24 24" {...rest}>
    <Path
      d="M3 3l18 18M9 9v6a3 3 0 004.5 2.6M12 4a3 3 0 013 3v3"
      fill="none"
      stroke={fill}
      strokeWidth={2}
    />
  </Svg>
);

<WithComponents overrides={{ icons: { Mute: CustomMuteIcon } }}>
  <Channel channel={channel}>
    <MessageList />
    <MessageInput />
  </Channel>
</WithComponents>;
```

Your icon is now used everywhere the SDK renders the mute icon within that subtree — for example, in the channel list preview and channel details.

### Overriding multiple icons

Add as many entries as you need. Sibling defaults are preserved, so this replaces only the send and thread-reply icons:

```tsx
<WithComponents
  overrides={{
    icons: {
      SendRight: CustomSendIcon,
      ThreadReply: CustomThreadReplyIcon,
    },
  }}
>
  <Channel channel={channel}>
    <MessageList />
    <MessageInput />
  </Channel>
</WithComponents>
```

<admonition type="info">

Icon overrides are merged per-icon: providing `icons={{ Mute: CustomMuteIcon }}` swaps the mute icon while leaving all other icons at their defaults. You never have to supply the full icon set.

</admonition>

### Using any component as an icon

A custom icon is just a React component that accepts `IconProps`. It doesn't have to be an SVG — you can render an `Image`, a component from another icon library, or anything else. Honor `height`/`width` so it lines up with the layout the SDK expects.

```tsx
import { Image } from "react-native";
import {
  WithComponents,
  Channel,
  MessageList,
  MessageInput,
  IconProps,
} from "stream-chat-react-native";

const ImageSendIcon = ({ height = 24, width = 24 }: IconProps) => (
  <Image source={require("./assets/send.png")} style={{ height, width }} />
);

<WithComponents overrides={{ icons: { SendRight: ImageSendIcon } }}>
  <Channel channel={channel}>
    <MessageList />
    <MessageInput />
  </Channel>
</WithComponents>;
```

## How icon props work

Every SDK icon accepts `IconProps`:

```tsx
type IconProps = Partial<SvgProps> & {
  height?: number;
  width?: number;
  size?: number;
  pathFill?: SvgProps["fill"];
  pathOpacity?: PathProps["opacity"];
};
```

When the SDK renders an icon it passes the appropriate size (`height`/`width`, or `size`) and color for the context — usually via `fill`, `stroke`, or `pathFill`, sourced from the active [theme](/chat/docs/sdk/react-native/customization/theming/). A well-behaved custom icon reads those values instead of hard-coding them, so it scales and recolors the same way the default did in each place it appears.

## Inheritance

Icon overrides follow the same inheritance rules as other `WithComponents` overrides. Inner providers merge over outer ones, so you can set app-wide icons at the top of the tree and override a specific icon deeper down:

```tsx
// App-wide default icons
<WithComponents overrides={{ icons: { SendRight: BrandSendIcon } }}>
  <Chat client={client}>
    {/* This channel uses a different send icon; everything else inherits BrandSendIcon */}
    <WithComponents overrides={{ icons: { SendRight: SpecialSendIcon } }}>
      <Channel channel={channel}>
        <MessageList />
        <MessageInput />
      </Channel>
    </WithComponents>
  </Chat>
</WithComponents>
```

## Reading the resolved icons

Inside your own custom components you can read the resolved icon set — defaults merged with any overrides — from `useComponentsContext()`. This lets a custom component reuse the same (possibly overridden) icons the SDK uses.

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

const MyCustomHeader = () => {
  const { icons } = useComponentsContext();

  return (
    <View>
      <icons.Search height={20} width={20} />
    </View>
  );
};
```


---

This page was last updated at 2026-07-17T15:25:26.245Z.

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