Render Jetpack Compose @Preview composables directly inside VS Code and Cursor — without touching your build, without Android Studio, without a language server.
Open a Kotlin file, press Cmd+Shift+V, see your previews. That's it.

Why this one
Other preview tools get speed by injecting their own Gradle plugin into your build and demanding the newest toolchain. This extension takes the opposite approach: it works with the project you already have.
| Jetpack Compose Preview | Plugin-injection preview tools | Android Studio | |
|---|---|---|---|
| Touches your build files | Never | Injects their own Gradle plugin | — |
| Gradle / AGP requirements | Yours (Gradle 8.x, AGP 8.5+ OK) | Gradle 9.4+, AGP 8.13+, Java 17 | Latest |
| Extra extensions required | None | Gradle for Java + Kotlin LSP | — |
| Real device / emulator rendering | Yes | No | Yes |
| Incremental rendering (only the active file's previews) | Yes | — | — |
| Works in Cursor and VS Code forks | Yes | Depends on LSP availability | — |
If your team is on a pinned Gradle version, behind a corporate proxy, or simply doesn't want third-party plugins inside the build — this is the preview tool that still works.
How it works
- Open a Kotlin file that contains
@Previewcomposables - Press
Cmd+Shift+V(or runCompose: Show Previewfrom the Command Palette) - The extension detects your Gradle project, runs Paparazzi, and renders the previews in a side panel
Under the hood:
- PreviewParser scans the file for
@Previewannotations (including stacked annotations and@PreviewParameter) - GradleRunner spawns
./gradlew :module:recordPaparazziDebugwith aCOMPOSE_PREVIEW_FILTERenv var so only the composables in your active file are rendered — not the whole project - PreviewPanel displays the resulting PNGs with their metadata (
widthDp,heightDp, device, background) - No Paparazzi in your project? The extension falls back to rendering on a connected emulator or real device via adb — 100% real pixels
Quick start
1. Add Paparazzi to your module
// app/build.gradle.kts
plugins {
id("app.cash.paparazzi") version "1.3.5"
}
2. Add a Paparazzi test class that reads COMPOSE_PREVIEW_FILTER
class ComposePreviewer {
@get:Rule
val paparazzi = Paparazzi(deviceConfig = DeviceConfig.PIXEL_5)
@Test
fun snapshots() {
val filter = System.getenv("COMPOSE_PREVIEW_FILTER")
?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }
fun shouldRender(name: String) = filter == null || name in filter
if (shouldRender("MyComposablePreview")) {
paparazzi.snapshot(name = "MyComposablePreview") { MyComposablePreview() }
}
// ... one entry per @Preview function in your project
}
}
See
sample/app/src/test/.../ComposePreviewer.ktfor a full working example, including@PreviewParameterand stacked-annotation variants. The "no Paparazzi detected" screen has a Copy Gradle snippet button with everything you need.
3. Open your project as a workspace folder
The extension searches workspace folders for a gradlew file. Open your Android project root (the folder containing gradlew) as a workspace folder.
Free vs Pro
We keep this simple and honest: everything manual is free, forever. The Pro license buys automation.
| Free | Pro | |
|---|---|---|
Manual preview (Cmd+Shift+V, refresh buttons, CodeLens) |
✅ | ✅ |
| Paparazzi + emulator/device rendering | ✅ | ✅ |
Stacked @Preview variants, @PreviewParameter, metadata |
✅ | ✅ |
| Auto-refresh on every save | 14-day trial | ✅ |
| Fast render daemon — save to preview in ~2-3s | 14-day trial | ✅ |
The daemon keeps Android's layout renderer warm in a persistent JVM: a warm render takes ~0.3s, plus ~2s of incremental compile (measured on the sample project). Without it, every save pays the full Gradle test-run cost (~10s warm). It still never touches your build files — the classpath is probed via a per-invocation --init-script, and any failure falls back silently to the standard Gradle path.
The renderer needs a one-time ~7s initialization. Rather than make you wait for it on your first preview, the daemon pre-warms in the background the moment a file with @Preview is focused — so by the time you open the preview, it's already warm. (Opt-out via composePreview.prewarm.)
- One-time purchase — not a subscription. 12 months of updates included.
- 14-day full trial, no signup required.
- 30-day offline grace — validated licenses keep working without network.
- Enter your key via
Compose: Enter License Keyor thecomposePreview.licenseKeysetting.
👉 Buy a license — or run Compose: Buy License from the command palette.
The license check is deliberately lightweight — no telemetry, no account, no DRM rootkits. We trust you; the gate exists so honest users have an easy way to support the tool.
Editor integration
- CodeLens — a "▶ Preview" action above every
@Previewfunction. Click it to render just that composable. No Kotlin language server required — unlike other tools, this works out of the box in any VS Code fork. - Gutter icons — every
@Previewfunction is marked in the gutter. - Focused preview — render one function at a time; "Show all" brings the rest back.
- Previewer generator —
Compose: Generate Previewer Testscans your module and writes theComposePreviewer.kttest class for you (shown as a button on the setup screen too).
Commands
| Command | Default keybinding |
|---|---|
Compose: Show Preview |
Cmd+Shift+V / Ctrl+Shift+V |
Compose: Refresh Preview |
— |
Compose: Generate Previewer Test |
— |
Compose: Enter License Key |
— |
Settings
| Setting | Default | Description |
|---|---|---|
composePreview.gradleProjectPath |
"" |
Override auto-detected project root |
composePreview.renderer |
"auto" |
auto / paparazzi / emulator |
composePreview.autoRefreshOnSave |
true |
Refresh preview on every file save (Pro after trial) |
composePreview.codeLens |
true |
Show "▶ Preview" CodeLens above @Preview functions |
composePreview.daemon |
"auto" |
Fast render daemon (Pro): auto / off |
composePreview.prewarm |
true |
Pre-warm the daemon in the background when an @Preview file is focused (Pro) |
composePreview.javaHome |
"" |
JDK for the daemon (default: JAVA_HOME, then java on PATH; needs 11+) |
composePreview.licenseKey |
"" |
Your Pro license key |
Requirements
- VS Code 1.90+ (or Cursor)
- An Android project with
gradlewat the workspace root - For Paparazzi rendering: Gradle ≤ 8.7 (Gradle 9 support pending upstream Paparazzi support), AGP 8.5+
- For emulator rendering: adb + a connected device or emulator
Known limitations
- Preview parsing is regex-based — edge cases like multiline annotation args may not be detected
- First Paparazzi run compiles the full module (~30-60s); subsequent runs are incremental (~10s warm, ~2s no-op)
- The
COMPOSE_PREVIEW_FILTERintegration requires a test class in your project (see Quick start, step 2) - Gradle 9.x is not yet supported by Paparazzi 1.3.5 — use Gradle ≤ 8.7
- Editing a transitive dependency of a
@PreviewParameterprovider isn't auto-detected — use manual refresh
Roadmap
- CodeLens & gutter icons — "▶ Preview" directly above each
@Previewfunction, no language server required - One-command previewer generation — scan your module and generate the
ComposePreviewertest class for you - Persistent render daemon — layoutlib kept warm between saves, ~2-3s save-to-preview (Pro)
- Background pre-warming — the cold layoutlib init is paid off-screen, before your first preview (Pro)
- MCP server for AI agents (Cursor, Claude Code)
Contributing / source-available
The source is visible for transparency and auditing, but this is not open-source software — see the license below.
git clone https://github.com/firezenk/compose-preview-vscode
cd compose-preview-vscode
npm install
Press F5 in VS Code to launch the Extension Development Host. The sample/ Android project opens automatically so you can test the full flow immediately.
License
Proprietary — see LICENSE. In short:
- Personal license for you, the purchaser
- 12 months of updates from the date of purchase
- No redistribution or resale
- Source visibility does not grant usage or modification rights
Note: v0.0.1 was published with an incorrect MIT license file by mistake; the proprietary EULA applies from v0.0.2 onward.