Every store size from one layout
Design the showcase once. iPhone, iPad and Play Store sizes all render from the same design.
Pixel-perfect for the store.
One capture. Every device.
Wired into your CI.
One screenstack.yml captures every screen and UI element on web, Flutter, iOS, Android and React Native, then fills your layouts.
Design the showcase once. iPhone, iPad and Play Store sizes all render from the same design.
Pixel-perfect for the store.
One capture. Every device.
Wired into your CI.
Each run covers devices, locales and appearance, so a German dark-mode set is never a manual job.
Reference a secret. The CLI swaps it for short-lived credentials at run time.
testUser: { secretRef: ssr_abc }The web runner captures your live site and dashboard at every viewport, so the previews on your homepage match what shipped.

The same schema drives the web, Flutter, iOS, Android and React Native runners. Pick yours.
version: 2
project: prj_123
testUser: { secretRef: ssr_abc }
locales: [en-US]
appearance: [light, dark]
platforms:
web:
baseUrl: http://localhost:4173
start: { command: npm run preview }
browsers: [chromium]
devices: [desktop-1440, iphone-16-pro]
login: { url: /login, waitFor: "[data-app-ready]" }
screens:
- id: home
route: /
auth: none
waitFor: "main h1"
- id: dashboard
route: /app
waitFor: "[data-screen=dashboard]"
elements:
- { id: revenue-card, web: "[data-ss=revenue]", padding: 16 }version: 2
project: prj_123
locales: [en-US, de-DE]
appearance: [light]
platforms:
flutter:
target: integration_test/screenstack_test.dart
devices: [iphone-16-pro, pixel-9]
screens:
- id: home
route: /
waitFor: { key: home_screen }
- id: workout
route: /workouts/strength
waitFor: { key: workout_screen }
elements:
- { id: set-row, flutter: set_row, padding: 12 }version: 2
project: prj_123
locales: [en-US]
appearance: [light, dark]
platforms:
ios:
scheme: MyApp
testPlan: ScreenStack
devices: [iphone-16-pro, ipad-pro-13]
screens:
- id: home
deepLink: myapp://home
waitFor: { testId: home-screen }
elements:
- { id: summary-card, testId: summary-card, padding: 12 }version: 2
project: prj_123
locales: [en-US]
appearance: [light, dark]
platforms:
android:
module: app
testClass: com.acme.ScreenStackTest
applicationId: com.acme.app
devices: [pixel-9]
screens:
- id: home
deepLink: myapp://home
waitFor: { testId: home-screen }
elements:
- { id: summary-card, testId: summary-card, padding: 12 }version: 2
project: prj_123
locales: [en-US]
appearance: [light]
platforms:
reactNative:
runner: detox
configuration: ios.sim.release
devices: [iphone-16-pro]
screens:
- id: home
route: /home
deepLink: myapp://home
waitFor: { testId: home-screen }
elements:
- { id: summary-card, testId: summary-card, padding: 12 }npx @voostack/screenstack-sdk run Routes or deep links for each screen, plus the cards and rows you want as separate layers.
The SDK captures every device, locale and appearance, then uploads the run to your project.
Bound slots pick up the new captures, so the next export already shows what you shipped.
Name an element by selector, Flutter key or test id. It comes back as a transparent layer and drops into the matching slot of every showcase.


[data-ss=auth-form]The ScreenStack MCP server lets Claude read your capture runs, fix captions and upload exports to both stores, after you approve.
ScreenStack for Claudeclaude mcp add --transport http --scope user screenstack https://api.screenstack.voostack.com/mcpEvery store size is on the free plan. Studio adds web capture, scheduled runs and team seats.