Coil

Loading Network Images with Coil

Coil 11,909 (github.com/coil-kt/coil (https://github.com/coil-kt/coil 11,909 ), Apache 2.0) is written in Kotlin on coroutines, and version 3 is Kotlin Multiplatform 25,990 . The current release is 3.6.3 (18 September 2026). Coil 3 moved networking into a separate artifact, so a network image needs io.coil-kt.coil3:coil-compose and coil-network-okhttp, plus the INTERNET permission. The test covers come from a Python server on the development machine, which the emulator reaches at 10.0.2.2, over HTTP that a network security config allows for that one host; Book.coverUrl builds each URL. An ImageRequest as AsyncImage's model configures the load, here with a listener that logs where each image came from:

ui/CoverImages.kt: a cover loaded with CoilKotlin
@Composable
fun CoilCover(book: Book, modifier: Modifier = Modifier) {
  AsyncImage(
    model = ImageRequest.Builder(LocalPlatformContext.current)
      .data(book.coverUrl)
      .listener(
        onSuccess = { _, result -> Log.d(TAG, "coil ${book.id}: ${result.dataSource}") },
        onError = { _, result -> Log.d(TAG, "coil ${book.id}: ${result.throwable}") },
      )
      .build(),
    contentDescription = null,                          // the title next to it says it all
    placeholder = painterResource(R.drawable.menu_book_24px),
    error = ColorPainter(Color(0xFFB0BEC5)),
    contentScale = ContentScale.Crop,
    modifier = modifier.size(80.dp, 120.dp).clip(coverShape)
      .background(MaterialTheme.colorScheme.surfaceVariant),
  )
}

AsyncImage measures itself first and decodes to about that size (80 by 120 dp is 210 by 315 pixels here), not the file's 400 by 600, which avoids the classic OutOfMemoryError of image lists.