compose-ai-tools
See your Compose UI without opening Android Studio.
compose-ai-tools renders your @Preview composables to PNG from the
command line — so your AI coding agent can actually look at the screen it
just changed, and so can you. Works with Jetpack Compose (Android) and
Compose Multiplatform Desktop.
That’s the whole idea. Everything else is optional.
Get started
Pick the one that fits you. Each is a single step.
🤖 With an AI coding agent
Run the one-line installer once. It drops the compose-preview CLI and
the agent skill into place (Claude Code, Codex, Gemini):
curl -fsSL https://raw.githubusercontent.com/yschimke/skills/main/scripts/install.sh | bash
Then just ask your agent to preview a composable. The
compose-preview skill
is the playbook — it tells the agent how to render, iterate, and check its
own work. You don’t have to learn the commands; the agent reads the skill.
Already have an agent that fetches URLs but can’t run the installer? Point it straight at the skill — it’s self-contained and will bootstrap the CLI itself.
🧩 In VS Code (or Cursor / Windsurf / VSCodium)
Open the Extensions view (⇧⌘X / Ctrl+Shift+X), search Compose Preview, click Install. That’s it — it renders previews inline and needs no project changes.
⌨️ From the command line
Install the CLI (same one-liner as above), then point it at any Compose project — no build edits required:
compose-preview render # render every @Preview to PNG
To browse those previews in a streamlined, Storybook-like component catalog:
compose-preview browse
This discovers every local preview-bearing module, starts a loopback-only server, and opens its
browser URL. Compatible CMP browser targets are built and connected automatically; other
components retain the baked snapshot and source-code experience. Pass --module :app only to
deliberately narrow the project-wide catalog.
The CLI injects itself into your build at runtime, so projects that already
use com.android.application / com.android.library /
org.jetbrains.compose just work. Full options on the
Install page.
Want a version-pinned Gradle plugin instead?
If you’d rather wire it into your build explicitly, the plugin is on Maven Central — no auth, no token:
// <module>/build.gradle.kts
plugins {
id("ee.schimke.composeai.preview") version "1.14.1"
}
./gradlew :app:composePreviewRenderAll # render every @Preview to PNG
Iterating on one screen? On Compose Desktop / Multiplatform-JVM modules you can narrow the render to a single preview (or a glob) so an unrelated broken preview can’t poison the run:
./gradlew :desktopApp:composePreviewRender --preview '*ExportHelpDialogPreview'
# or, as a Gradle property: -PcomposePreview.filter='*ExportHelpDialogPreview'
--preview selects whole @Preview functions. To skip one member of a
function’s fan-out — a multipreview member, or one of several @Preview
annotations, which share a function name but have distinct ids — exclude it by id
instead:
./gradlew :desktopApp:composePreviewRender --exclude-preview-id '*_Dark'
# or, as a Gradle property: -PcomposePreview.idExclude='*_Dark'
Ids take the same pattern syntax as --preview (a */? glob, or a plain
substring). --preview-id is the positive form (“render only these”); prefer
--exclude-preview-id when you’re deferring part of a fan-out, since a stale
pattern then renders too much rather than dropping a preview you wanted. Either
way a filtered run is never stored in the build cache, since its render directory
is only part of the module’s output.
One fan-out has no ids to name: a @PreviewParameter provider’s rows exist only
once the renderer enumerates the provider, so discovery lists the parameterized
function as a single preview. Those are skipped by label — the token in
<stem>_<label>.png, matched case-insensitively:
./gradlew :desktopApp:composePreviewRender --exclude-preview-row 'Dark,ExtraDark'
# or, as a Gradle property: -PcomposePreview.rowExclude='Dark,ExtraDark'
A row exclusion never empties a preview’s rows: if every row matches, they all render, so nothing can silently produce a preview with no pixels.
All four filters work on both backends. An Android module renders through a
Robolectric test task rather than this desktop one, and it reads the same options
and the same composePreview.* properties — #2977 closed the gap where they were
desktop-only.
Requirements and CI recipes live on the Install page.
Going further
Once you’re rendering PNGs, there’s more your agent can do — but none of it is required to get value from the tool.
- Data products — alongside each PNG the renderer can emit structured data: accessibility findings, layout trees, theme tokens, recomposition heat maps, drawn text, and more. One page per product.
- MCP server — a push-based, token-frugal agent loop over Compose UI (target by semantic ref, observe semantics instead of pixels, diff renders, generate tests). The aria-snapshot story for Compose.
- Daemon — a long-lived renderer that keeps Robolectric / Compose-Desktop warm so re-renders are fast.
More
- How it works — discovery, renderer pipeline, caching.
- Samples — rendered baselines for
samples:android,samples:wear,samples:cmp,samples:remotecompose, regenerated on every push tomain. - Releases · Changelog · License (Apache 2.0).
</content> </invoke>