Landscapist: A Pluggable Image Loader for Jetpack Compose and Kotlin Multiplatform
🌻 A pluggable, lightweight, highly optimized Jetpack Compose and Kotlin Multiplatform image loading library that fetches and displays network images.
At a glance
- What is it?
- Landscapist wraps Glide, Coil and Fresco behind one Compose API and adds its own plugin layer. It is aimed at Android and KMP teams who want image loading behaviour to be configurable rather than hard-coded.
- Who is it for?
- Adopt Landscapist if you already ship Compose or Kotlin Multiplatform and want image loading behaviour expressed as plugins, options and composable slots rather than scattered across call sites. Skip it if you only need a single loader with no wrapping layer, or if your team cannot absorb the per-engine artifacts and the extra indirection.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Landscapist Is For, and Who Should Care
Most Compose image loading starts simple: one composable, one URL, one engine. It stops being simple when the same app needs a placeholder in one place, a crossfade in another, a blur transform in a third, and a loading-state callback that drives a shimmer. At that point the loading logic either spreads across every call site or gets wrapped in a house abstraction.
Landscapist is that abstraction, published. The README describes it as a pluggable Jetpack Compose and Kotlin Multiplatform image loading solution that fetches and displays network images, and it states compatibility with Glide, Coil and Fresco. The repository layout confirms this is not a single artifact: there are separate top-level directories for coil, coil3, glide, fresco and fresco-websupport, alongside landscapist-core, landscapist-image, landscapist-animation, landscapist-transformation, landscapist-palette, landscapist-zoomable, landscapist-placeholder, landscapist-svg and landscapist-image-gallery.
The intended reader is an Android or Kotlin Multiplatform engineer who already has an engine and does not want to give it up. Landscapist does not ask you to replace Glide or Coil. It asks you to describe what you want in Compose terms and let the plugin layer translate that into whichever engine you already depend on. Teams that have no engine preference and just want the shortest path to a rendered image will find the wrappers to be overhead.
How the Plugin and ImageComponent Layer Actually Works
The README names two concepts that carry the design: ImageOptions and ImageComponent with ImagePlugin. The pattern is a composition rather than inheritance. You supply options describing the request, and plugins attach behaviour around the loading lifecycle. The README also lists listening to image state changes as a first-class feature, which is what makes the plugin model useful: a plugin can observe state transitions rather than only decorate the final bitmap.
The second mechanism is recomposition cost. The README states that most composable functions in Landscapist are Restartable and Skippable, and points to Compose compiler metrics to support that. That matters because a naive wrapper around an image loader often ends up non-skippable, which means the wrapper recomposes whenever its parent does. Landscapist also states that performance was improved with Baseline Profiles. The repository includes benchmark-landscapist, benchmark-landscapist-app and benchmark-engine directories, so the project carries its own benchmarking setup rather than relying on anecdote.
The third layer is the visual features, which are separate modules rather than flags on the core call: crossfade and circular reveal animations, blur transformation, palette extraction, zoomable images, SVG support and an image gallery. Splitting these out means you pull in only what you use, at the cost of tracking more artifacts in your build.
Installing Landscapist and Rendering a First Network Image
The README does not print a Gradle snippet in the text available here; it directs readers to the official documentation at skydoves.github.io/landscapist for comprehensive information. The repository layout is the reliable guide to what you depend on. There is a version-catalog directory and a bom directory, which indicates a Bill of Materials is published so that Landscapist artifact versions can be aligned from one coordinate.
A typical setup therefore adds the BOM plus the engine-specific artifact matching the loader you already use. The artifact directory names are coil, coil3, glide, fresco and fresco-websupport, and the core Compose entry point lives in landscapist-image. Confirm the exact coordinates and current version in the official documentation before editing a build file.
Once the dependency resolves, the smallest useful call is the image composable from landscapist-image, given a URL. The README points to a browser playground at skydoves.github.io/landscapist/demo, which it describes as landscapist-image compiled to WebAssembly. That playground is the fastest way to see what each image size, content scale and plugin combination does before writing any Kotlin, and it prints the Kotlin for whatever combination you land on, with a button to copy it into your project. If you are evaluating the library rather than committing to it, start there.
Where Landscapist Gets in the Way
The wrapping layer is the main cost. Because Landscapist sits between your composable and Glide, Coil or Fresco, any engine feature that has not been surfaced through ImageOptions or a plugin is either unreachable or requires writing a custom ImageComponent. The README presents custom composables and custom implementations as supported paths, which is honest, but it means the escape hatch is code you own and maintain.
The module split is the second cost. Animation, transformation, palette, zoomable, placeholder, SVG and image gallery are separate directories, so a project that wants crossfade plus blur plus palette is adding three artifacts on top of the core and the engine adapter. Each one is a version to track, though the BOM exists precisely to reduce that burden.
Third, the multiplatform claim needs scoping against your targets. The README describes the library as Jetpack Compose and Kotlin Multiplatform, and the repository contains kotlin-js-store and demo-web, so JavaScript or WebAssembly is clearly in scope. It does not enumerate which of the optional modules are published for every target. If you need palette or SVG on a non-Android target, verify publication before designing around it.
Finally, if you have exactly one image-loading call site with no placeholder, no animation and no state observation, Landscapist is the wrong tool. You would be adding a dependency, a BOM and an engine adapter to solve a problem you do not have.
Landscapist and Coil: Different Jobs, Overlapping Ground
Coil is an image loading library for Kotlin, and Landscapist lists compatibility with it. The difference in approach is that Coil is the loader, while Landscapist is a Compose-facing layer that can drive Coil, Glide or Fresco. If you adopt Coil directly, you get its API and its caching behaviour with no intervening abstraction. If you adopt Landscapist with the coil artifact, you get Landscapist's composables, ImageOptions, plugin hooks and animation modules, with Coil doing the fetching underneath.
That makes the choice less about which loader is better and more about whether you want the layer. A team standardising on one engine and one Compose API will find Landscapist useful precisely because the engine becomes swappable: the same composable call can be backed by Glide in one module and Coil in another, which is a real option for codebases migrating between loaders. A team that has already built its own Compose wrapper, or that depends on a Coil feature Landscapist has not surfaced, gains less and inherits a second API surface.
The honest framing is that Landscapist and Coil are not competitors at the same level. Landscapist's value is the abstraction and the plugin model, and it is only worth the indirection if you actually use that abstraction across more than a handful of screens.
Maintenance, Releases and the Apache-2.0 Licence
The repository is not archived, and the last push was on 2026-09-23. Releases are frequent: 2.13.0 on 2026-09-09, 2.13.1 on 2026-09-13, and 2.13.2 on 2026-09-23. A patch release landing on the same day as the last push suggests the project responds to issues rather than accumulating them. There is also a renovate.json at the repository root, which indicates automated dependency updates, and a .coderabbit.yaml, which indicates automated review configuration. Both are signals about process, not about code quality.
Upgrade cost is dominated by the module count. Because the BOM exists, aligning versions is a single coordinate change, but you still need to confirm that each optional module you depend on has a matching release. The version-catalog directory suggests the project also publishes a Gradle version catalog, which would let you reference Landscapist versions by alias.
The licence is Apache-2.0, as stated in the README badge and the LICENSE file at the repository root. Apache-2.0 is a permissive licence that includes an explicit patent grant, which is generally the reason Android teams prefer it over MIT for libraries that may end up in shipped products. It also requires that you preserve the licence notice and state significant changes. This is a description of the licence text, not legal advice; route anything specific to your situation through your own counsel.
Editorial conclusion
Adopt Landscapist if you already ship Compose or Kotlin Multiplatform and want image loading behaviour expressed as plugins, options and composable slots rather than scattered across call sites. Skip it if you only need a single loader with no wrapping layer, or if your team cannot absorb the per-engine artifacts and the extra indirection. Before committing, verify which engine artifact matches your existing stack, check that the composables you depend on are Restartable and Skippable in your own Compose compiler metrics, and confirm the animation and transformation modules you plan to use are published for your target platforms.
Frequently asked questions
What is Landscapist and what does it do?
Landscapist is a pluggable Jetpack Compose and Kotlin Multiplatform image loading library that fetches and displays network images. It is compatible with Glide, Coil and Fresco, and adds its own layer for image options, plugin-based behaviour, and animations such as crossfade and circular reveal.
How does Landscapist compare with Coil?
Coil is an image loading library, while Landscapist is a Compose-facing layer that can drive Coil, Glide or Fresco underneath. Choosing Landscapist means adopting its composables, ImageOptions and plugin model on top of whichever engine you already use.
Can I try Landscapist without installing anything?
Yes. The README points to a playground at skydoves.github.io/landscapist/demo, which it describes as landscapist-image compiled to WebAssembly and running in the page. It lets you switch image size, content scale and plugin combinations and copies out the matching Kotlin.
Is Landscapist still maintained?
The repository is not archived, and the last push was on 2026-09-23. Releases 2.13.0, 2.13.1 and 2.13.2 shipped between 2026-09-09 and 2026-09-23.
What licence does Landscapist use?
The README badge and the LICENSE file at the repository root both identify Apache-2.0. That is a permissive licence, and the README does not state any additional terms.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/skydoves-landscapist)