# Customizing Image Loading

The Compose SDK loads images, GIFs, and video thumbnails through [Coil 3](https://coil-kt.github.io/coil/). The loader is exposed on `ChatTheme` via the `imageLoaderFactory` property — defaulting to `StreamCoilImageLoaderFactory.defaultFactory()` — so you can customize image loading globally without re-implementing any rendering code.

This page describes what the default factory provides and how to safely extend or replace it.

## StreamCoilImageLoaderFactory

`StreamCoilImageLoaderFactory.defaultFactory()` provides the default factory that creates new Coil `ImageLoader` instances.

You can find out more about it by reading the [class documentation](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/StreamCoilImageLoaderFactory.kt).

You can customize how images are loaded by passing your own implementation of `StreamCoilImageLoaderFactory` to `ChatTheme`, or by using the default factory to provide an `ImageLoader` and calling `loader.newBuilder()` to expand or override its behavior.

## Default Coil Components

Starting with **6.13.0**, the SDK runs on Coil 3. The built-in `StreamImageLoaderFactory` registers:

- `OkHttpNetworkFetcherFactory` — fetches image bytes over HTTP/HTTPS. Registered explicitly in code so it is not affected by R8 full-mode stripping the Coil 3 `ServiceLoader` registration.
- `AnimatedImageDecoder` (API 28+) / `GifDecoder` — decodes GIF and animated WebP attachments, including Giphy.
- `VideoFrameDecoder` — decodes the first frame of video attachments to display video thumbnails.

If you provide a fully custom `StreamCoilImageLoaderFactory` without delegating to `StreamImageLoaderFactory`, you must re-register these components yourself, otherwise GIFs render as static frames, video thumbnails go missing, and HTTP fetching can fail under R8 full mode.

## Customizing the Component Registry

`ImageLoader.Builder.components { … }` **replaces** the existing component registry on the builder — it is not additive. Because the `StreamImageLoaderFactory` builder lambda runs after the SDK populates its own components, any `components { … }` block you supply overwrites the SDK defaults.

If you need a custom Coil component (decoder, mapper, or network fetcher), re-register every component the SDK provides alongside your additions:

```kotlin
val factory = StreamCoilImageLoaderFactory { context ->
    StreamImageLoaderFactory {
        components {
            // SDK defaults — must be re-registered when you supply your own components block.
            add(OkHttpNetworkFetcherFactory())
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                add(AnimatedImageDecoder.Factory(enforceMinimumFrameDelay = true))
            } else {
                add(GifDecoder.Factory(enforceMinimumFrameDelay = true))
            }
            add(VideoFrameDecoder.Factory())

            // Your additions:
            add(MyCustomDecoder.Factory())
        }
    }.newImageLoader(context)
}

ChatTheme(imageLoaderFactory = factory) {
    // Your UI content
}
```

<admonition type="info">

**Need auth headers or signed URLs on image requests?** Use the [`CDN`](/chat/docs/sdk/android/v6/client/guides/custom-cdn/) interface instead of customizing Coil. `CDN.imageRequest` applies to every image URL the SDK loads and avoids the need to replace the loader's component registry.

</admonition>

## Troubleshooting: blank or placeholder images

<admonition type="note">

Recent 6.x releases register the OkHttp fetcher in code, so apps on the default loader are not affected. Manual component registration only matters if you are pinned to an older 6.x release (6.13.0 through the version that shipped this fix) **or** you provide a fully custom `ImageLoader`.

Symptoms are typically release-build-only with R8 enabled, because Coil 3 auto-registers the fetcher through `ServiceLoader`, which R8 full-mode can strip. The snippet under [Customizing the Component Registry](#customizing-the-component-registry) is the canonical component list — register the OkHttp fetcher there explicitly to bypass auto-registration.

</admonition>


---

This page was last updated at 2026-05-07T12:01:41.237Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/android/v6/compose/general-customization/image-loading/](https://getstream.io/chat/docs/sdk/android/v6/compose/general-customization/image-loading/).