Getting started
Install from source, add a setup route, and capture one screen.
Requirements
- macOS with Xcode and an installed iOS Simulator runtime.
- Node.js 22 or newer, pnpm 9.15.9 for the Snapbot checkout, and CocoaPods (
pod). - An Expo Router app with
expo-dev-clientand its JavaScript dependencies installed. - A working app URL scheme, iOS bundle identifier, Xcode scheme, and app product name.
Snapbot runs Expo prebuild and CocoaPods in your app before building. Commit or otherwise preserve app changes before your first capture so generated native changes are easy to review. It creates or uses a dedicated simulator and shuts that simulator down when the run finishes.
Install from source
The npm names snapbot and react-native-snapbot are not published as of
November 8, 2026. Use the repository and a local package tarball; bare
npm install snapbot or npx snapbot is not a working installation path yet.
git clone https://github.com/twocentinc/snapbot.git
cd snapbot
pnpm install --frozen-lockfile
npm pack --ignore-scripts --workspace=false ./packages/react-native-snapbotThe last command creates react-native-snapbot-0.1.0.tgz in the checkout. In your
Expo app directory, install that tarball with your app's package manager:
npm install /absolute/path/to/snapbot/react-native-snapbot-0.1.0.tgzThe app must already provide expo-router, react, and react-native at
versions compatible with its Expo SDK. Keep the tarball available wherever you
install the app, or place it in an app-owned vendor directory and use a relative
path for reproducible team installs.
Add a minimal setup route
Create app/snapbot.tsx (or src/app/snapbot.tsx if your app uses that route
root). Start with a public screen that needs no login or app-specific theme:
import { SnapbotRoute } from "react-native-snapbot";
const configure = async () => true;
export default function SnapbotScreen() {
return (
<SnapbotRoute
apiEnvironment={process.env.EXPO_PUBLIC_API_ENV ?? "development"}
configure={configure}
/>
);
}Check that auth redirects or parent layouts allow the debug setup route to render.
In production builds SnapbotRoute redirects to / without running setup.
For a custom theme or account, extend the callback using
app integration.
Configure one screen
Save this as snapbot.json in your app root. Replace the example identifiers and
simulator values with your app and an installed runtime. These values are
examples, not defaults:
{
"name": "example-app",
"appDirectory": ".",
"bundleIdentifier": "com.example.app",
"scheme": "example",
"xcodeScheme": "ExampleApp",
"iosProduct": "ExampleApp.app",
"apiEnvironment": "development",
"appearance": "light",
"simulator": {
"name": "Snapbot Example",
"deviceType": "iPhone 17 Pro",
"runtime": "iOS 26.5"
},
"metro": { "port": 8090 },
"defaults": {
"minimumWaitMs": 750,
"sampleIntervalMs": 250,
"stableSamples": 3,
"timeoutMs": 15000
},
"screenshots": [
{ "name": "home", "deeplink": "example://home" }
]
}Use xcrun simctl list runtimes and xcrun simctl list devicetypes to inspect
installed simulator choices. Use a simulator name dedicated to Snapbot.
xcodeScheme also selects <xcodeScheme>.xcworkspace; iosProduct must match
the .app produced by your debug simulator build.
Capture
From the Snapbot checkout, run:
pnpm capture \
--project-root /absolute/path/to/your-expo-app \
--config snapbot.json \
--output snapbot-output/first-runAll relative paths resolve from --project-root, including the config path,
appDirectory, and output. The output directory must be new or empty.
Open snapbot-output/first-run/home.png, then inspect summary.json and
run.log. The first build can take longer than subsequent captures. For another
run, choose a fresh output path. Add appearance: "both" to capture light and
dark variants, and add screenshot entries for more routes.
Read CLI and output before treating exit zero as an acceptance check, and troubleshooting if the first screenshot shows an overlay, loading screen, or setup error.