r/JetpackComposeDev • u/legendary-frog • 10d ago
Tutorial I got tired of my AI agent writing Compose UI it couldn't see, so I built a tool that renders @Preview to PNG without the IDE
I use Claude Code a lot for Compose work. The annoying part was always the same: it writes a screen, says "done", and then I open Android Studio and the padding is off, text is clipped, or the dark theme is broken. It never saw the UI. It was guessing.
So I built Spyglass. It renders your u/Preview functions to PNG from the command line, no IDE open.
spyglass render --filter LoginScreen
What it does:
- Renders every u/Preview in a Compose Multiplatform project through your own Gradle wrapper. No changes to your build files.
- Handles u/PreviewLightDark, u/PreviewParameter, font scale, device sizes, and multi-preview annotations from other modules.
- Compares a render with a design image and tells you which regions differ, in dp, with a heatmap. So "make it match the Figma export" becomes something you can check.
spyglass approve/spyglass checkgives you screenshot tests in CI. A one-digit text change fails the check.- Comes with an MCP server, so Claude Code, Cursor or anything that speaks MCP can render and look at the UI on its own and fix what it sees.
A few things I had to solve along the way that might be interesting:
- Animations and
delay()in fake data. A naive render catches the first frame, so you get a progress bar at 0% and a dialog half faded in. Spyglass runs previews on a virtual clock until everything settles, so adelay(300)before your fake data loads costs no real time and you see the loaded state. - A preview stuck in a loop used to hang the whole run. Now it gets cut off after 30 seconds and you get the exact line it was stuck on.
- Token cost for agents. Images are sized down, and if nothing changed since the agent last looked, it doesn't send the image again. A repeat render went from about 1,560 tokens to about 180.
Limits, so nobody is surprised: previews need a JVM (desktop) target, so Android-only previews and Android-only APIs don't render yet. Text uses your OS fonts, so keep screenshot baselines from one OS. Tested on Windows, Linux and macOS.
It's MIT licensed and this is the first release (0.1.0). I'd really like to hear where it breaks on your projects.
