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 Reference GitHub


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.

Marketplace · Open VSX

⌨️ 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 to main.
  • Releases · Changelog · License (Apache 2.0).

</content> </invoke>


Apache 2.0 licensed. Source on GitHub.

This site uses Just the Docs, a documentation theme for Jekyll.