compose-preview
A terminal UI for browsing and running Jetpack Compose @Preview composables — without launching Android Studio.

The problem
You want to check a Compose preview. You open Android Studio. You wait 2 minutes for it to index. You navigate to the file. You wait for the preview to render. All that just to see a button in dark mode.
compose-preview scans your project for @Preview functions and lets you browse and launch them on a connected device — directly from the terminal, in seconds.
Screenshot
Compose Preview Browser — umobKMP · Pixel_6a
/ Filter: login
╭ 1 Modules ──────────╮╭ 2 Previews (8) ──────────────────────╮╭ 3 Preview ─────────╮
│ ▸ :composeApp (0) ││ ▸ LoginScreenEmptyDarkPreview ││ │
│ :feature:auth (8) ││ LoginScreenEmptyLightPreview ││ ┌───────────┐ │
│ :feature:booking ││ LoginScreenFilledDarkPreview ││ │ │ │
│ (0) ││ LoginScreenLoadingDarkPreview ││ │ preview │ │
│ :feature:home (0) ││ LoginScreenErrorDarkPreview ││ │ image │ │
│ :feature:map (0) ││ LoginFormContentEmptyDarkPreview ││ │ │ │
│ :feature:settings ││ LoginFormContentFilledDarkPreview ││ └───────────┘ │
│ (0) ││ LoginFormContentLoadingDarkPreview ││ │
│ ││ ││ w for HD preview │
│ ││ ││ in browser │
╰──────────────────────╯╰──────────────────────────────────────╯╰─────────────────────╯
╭ Details ────────────────────────────────────────────────────────────────────────────╮
│ FQN: com.example.feature.auth.LoginScreenPreviewKt.LoginScreenEmptyDarkPreview │
│ File: feature/auth/src/androidMain/.../preview/LoginScreenPreview.kt:42 │
│ Params: showBackground=true, backgroundColor=0xFF111111 │
╰─────────────────────────────────────────────────────────────────────────────────────╯
● Launched: LoginScreenEmptyDarkPreview (com.example.app.dev)
enter run · s screenshot · f fullscreen · w web · i install · / filter · d device · x kill emu · q quit
What it does
- Scan — Discovers all
@Preview composables across all Gradle modules automatically
- Browse — Navigate modules and previews in a three-panel TUI with keyboard and mouse
- Search — Live filter bar (
/) matches preview names across all modules, counts update in real time
- Run — Launch any preview on a connected device via ADB with
Enter
- Screenshot — Capture a preview screenshot (
s) displayed directly in the terminal
- Fullscreen HD — Press
f to view the screenshot fullscreen using your terminal's native graphics protocol (Kitty, iTerm2, WezTerm, Ghostty), navigate between previews with ↑/↓
- Crash detection — Automatically detects when a preview crashes and shows the root cause error in the preview panel
- HD Web Preview — Press
w to open a local web viewer in your browser with full-quality preview rendering
- Install — Trigger Gradle install tasks (
i) with automatic variant detection (dev, qa, accept, production)
- Device / Emulator picker — Press
d to select a connected device or launch an AVD emulator, with optional fast mode (headless + Quick Boot)
- Kill emulator — Press
x to list running emulators and kill one (useful for headless emulators)
- Details — See fully qualified name, file path, line number, and
@Preview parameters
- Composable count — Shows total
@Composable functions per module, helping identify preview coverage gaps
- Stale detection — Warns when source files are newer than the installed APK
Install
Homebrew (macOS & Linux)
brew tap ignaciotcrespo/tap
brew install compose-preview-cli
Go
go install github.com/ignaciotcrespo/compose-preview-cli/cmd/compose-preview@latest
Binary download
Grab the latest release from GitHub Releases.
Usage
# From your Android/KMP project directory:
compose-preview
# Or specify a path:
compose-preview /path/to/android/project
# Open HD web preview in browser:
compose-preview --web
# Web preview on a custom port (default: 9999):
compose-preview --web --port 8080
# List all previews as JSON (for scripting/CI):
compose-preview --list
# Run a specific preview on a connected device:
compose-preview --run SplashScreenPreview
# Take a screenshot of a preview (saves to preview.png):
compose-preview --screenshot SplashScreenPreview
# Custom output file and render delay (default: 1s):
compose-preview --screenshot SplashScreenPreview --output splash.png --delay 3
# Clear cached screenshots:
compose-preview --clear
# Dismiss "built for older Android" dialog automatically:
compose-preview --dismiss-dialog
Layout
┌──────────────────────────────────────────────────────────────────────────────┐
│ Compose Preview Browser — <project> · <device> (d to change) │ header
├──────────────────────────────────────────────────────────────────────────────┤
│ / Press / to filter previews │ search bar
├──────────────┬──────────────────────────┬────────────────────────────────────┤
│ 1 Modules │ 2 Previews (88) │ 3 Preview │
│ │ │ │
│ ▸ :app (2) │ ▸ AppAndroidPreview │ ┌────────────┐ │
│ :feature: │ AppPreview │ │ preview │ │
│ auth (30)│ │ │ screenshot│ screenshot panel│
│ :feature: │ │ └────────────┘ │
│ home (11)│ │ │
│ ... │ │ w for HD preview in browser │
├──────────────┴──────────────────────────┴────────────────────────────────────┤
│ Details │
│ FQN: com.example.MainActivityKt.AppAndroidPreview │ details
│ File: composeApp/src/androidMain/.../MainActivity.kt:41 │
├──────────────────────────────────────────────────────────────────────────────┤
│ ⚠ sources changed since last build — press 'i' to install │ status
├──────────────────────────────────────────────────────────────────────────────┤
│ enter run · s screenshot · f fullscreen · w web · i install · / filter · d device · x kill emu · q quit │ help
└──────────────────────────────────────────────────────────────────────────────┘
Key bindings
| Key |
Action |
/ |
Focus search bar — type to filter previews live |
Tab |
Exit search / switch between Modules and Previews panels |
Enter |
Run selected preview on device (auto-captures screenshot) |
Esc |
Clear filter and exit search |
j/k or ↑/↓ |
Navigate items in focused panel |
s |
Capture screenshot of the selected preview |
f |
Fullscreen HD preview with native terminal graphics — ↑/↓ to navigate between previews |
w |
Toggle HD web preview viewer in browser |
i |
Install APK via Gradle (auto-detects build variants) |
d |
Open device / emulator picker (press f in picker to toggle fast mode) |
x |
Open kill emulator picker — select a running emulator to stop it |
R |
Refresh project scan |
1 / 2 |
Focus Modules / Previews panel directly |
q |
Quit |
| Mouse click |
Select item in any panel |
| Mouse wheel |
Scroll within panels |
Search / Filter
Press / to activate the search bar. As you type, previews are filtered across all modules in real time:
/ Filter: dark█
╭ 1 Modules ─────────────╮╭ 2 Previews (23) ─────────────────────────────────╮
│ ▸ :feature:auth (12) ││ ▸ LoginScreenEmptyDarkPreview (Login - Dark) │
│ :feature:home (5) ││ LoginScreenFilledDarkPreview (Login - Filled) │
│ :feature:map (4) ││ LoginScreenLoadingDarkPreview (Login - Loading)│
│ :feature:settings (2) ││ LoginScreenErrorDarkPreview (Login - Error) │
│ :composeApp (0) ││ ... │
╰─────────────────────────╯╰──────────────────────────────────────────────────╯
type to filter · tab panels · esc clear · enter confirm
Module counts update to show only matching previews. Press Tab to move to the panels with the filter active, or Esc to clear it.
Screenshots
Press s to capture a screenshot of the selected preview. The screenshot is rendered directly in the terminal using half-block characters. Screenshots are cached — a dot marker (◉) next to a preview name indicates a cached screenshot.
Running a preview with Enter also auto-captures a screenshot after a short delay.
Press f to enter fullscreen HD mode using your terminal's native graphics protocol for pixel-perfect quality. Use ↑/↓ to navigate between previews — cached screenshots display instantly, uncached ones are captured on the fly. Supported terminals: Kitty, iTerm2, WezTerm, Ghostty. Other terminals fall back to half-block rendering. Press any other key to return to the TUI.
The --screenshot CLI command also displays the image inline using the native graphics protocol.
Crash detection
When a preview crashes on the device, the error is automatically detected from logcat and displayed in the preview panel with the root cause exception and source location. This replaces the blank/broken screenshot you would otherwise see.
HD Web Preview
The terminal screenshot is low resolution. For full-quality rendering, press w to start a local web viewer. This opens your browser with an HD version of the preview, served from a local web server. Press w again to stop the server.
Device / Emulator picker
Press d to open a modal listing connected devices and available AVD emulators. Select a device to target, or pick an emulator to launch it.
Press f inside the picker to toggle fast mode — launches the emulator headless (no window, no audio) with Quick Boot snapshots and host GPU acceleration. This is significantly faster than a cold boot and ideal for CI or when you don't need the emulator UI.
Kill emulator
Press x to open a modal listing all running emulators. Select one and press Enter to kill it. This is especially useful for headless emulators launched in fast mode, which have no window to close.
╭ Kill Emulator ───────────────────────╮
│ ▸ Pixel_6_API_34 (emulator-5554) │
│ Pixel_7_API_35 (emulator-5556) │
│ ↑↓ navigate · enter kill · esc cancel│
╰──────────────────────────────────────╯
Install variants
When you press i, compose-preview queries Gradle for all available install tasks. If your project has multiple build variants (dev, qa, accept, production), a picker modal appears:
╭ Select Install Task ─────────────╮
│ ▸ installDevDebug │
│ installAcceptDebug │
│ installQaDebug │
│ installProductionDebug │
│ ↑↓ navigate · enter select · esc │
╰──────────────────────────────────╯
Select a task with Enter. The choice is remembered for subsequent installs.
Requirements
- ADB on your PATH (from Android SDK platform-tools)
- A connected Android device or emulator
- The debug APK must be installed on the device (use
b to build+install)
androidx.compose.ui:ui-tooling as a debugImplementation dependency in your app module:
// In your app's build.gradle.kts
debugImplementation("androidx.compose.ui:ui-tooling")
How it works
- Discover — Walks your Gradle project to find modules via
build.gradle.kts files
- Scan — Parses
.kt files for @Preview annotations using regex (fast, no compilation needed)
- Resolve — Extracts package name, JVM class name (
FileNameKt), function name, and preview parameters
- Launch — Sends
adb shell am start -W with the composable FQN to PreviewActivity (waits for Activity to be displayed)
- Detect — Auto-discovers the installed app package, trying all flavor variants
Works with both pure Android and Kotlin Multiplatform (KMP) projects.
Supported project structures
- Single-module Android projects
- Multi-module Android projects with feature modules
- Kotlin Multiplatform (KMP) projects with
composeApp module
- Projects with product flavors (dev, qa, staging, production, etc.)
- Projects using
applicationId, namespace, or both
License
MIT