Skip to Content
DocsStart hereQuickstart

Quickstart

From a freshly cloned Expo app to a passing flow in under five minutes.

The contract is one terminal, one command, one green tick.

Prerequisites

  • Node ≥ 20
  • Expo SDK 54+ with the New Architecture enabled (Bridgeless / Fabric, default since RN 0.74). The runtime probes global.RN$Bridgeless at startup and refuses to register on Old Architecture.
  • Xcode (for iOS) or Android Studio (for Android) installed on your host machine.
  • A booted simulator or attached device. xcrun simctl boot "iPhone 16 Pro" for iOS; emulator -avd <name> for Android.

That’s it. No API keys, no native module setup, no XCUITest project scaffolding.

Run klera init

From the root of your Expo app:

cd my-expo-app npx @klera/cli init

init is opinionated. It detects your Expo SDK, picks the right native driver packages, installs them with the package manager you already use, codemods your entry file to wrap the root in <KleraRuntimeProvider>, scaffolds a smoke flow, scaffolds the project’s klera config, and runs klera doctor inline before it hands control back to you.

Output looks like:

✓ detected Expo SDK 54.0.0 (NewArch: enabled) ✓ platforms: iOS + Android ✓ scaffold flows/welcome.flow.md (prose) ✓ scaffold flows/welcome.flow.json (pre-baked IR cache) ✓ scaffold .klera/config.yaml ✓ wrap App.tsx root in <KleraRuntimeProvider> ✓ install klera, @klera/runtime, @klera/native-driver-ios, @klera/native-driver-android via pnpm add -D ... running klera doctor: ✓ Node ≥ 20 v20.x.x ✓ dist/ artefacts cli, mcp, engine built ✓ iOS Simulator 1 booted ✓ DeviceDriver native linked + reachable ✓ New Architecture Bridgeless detected ✓ Planner transport claude (subscription-paid)

init is idempotent. Re-running on an already-set-up project shows for steps that already applied. Useful flags:

  • --dry-run — print the plan without touching disk.
  • --force — re-apply every step, overwriting existing scaffolds.
  • --no-install — skip the install step (yarn workspaces, lockfile bots, etc.).
  • --no-doctor — skip the inline doctor invocation.
  • --yaml — opt back into the YAML scaffold instead of prose.

If klera init cannot codemod your entry file (exotic root-component shape), it writes .klera/manual-patch.md with the exact snippet to apply by hand. The codemod handles the unmodified npx create-expo-app template plus the most common variants.

Look at the scaffolded flow

flows/welcome.flow.md:

# Welcome smoke Wait for the welcome screen to settle (animations and network idle), assert that the text "Welcome" is visible, then capture a visual snapshot called "welcome".

The paired flows/welcome.flow.json cache compiles down to three IR steps:

  1. waitForIdle — wait for animations and network requests to settle. On a freshly launched app this gives the welcome screen time to render before the assertion fires.
  2. assert — match any element whose text contains “Welcome” via the matcher’s strategy ladder (testID → accessibilityLabel → role+text → fuzzy text).
  3. visualSnapshot — capture the current screen as a baseline PNG on first run; pixel-diff against it on subsequent runs.

The first run consumes the pre-baked cache — no LLM call needed.

Run your first flow

pnpm exec klera run flows/welcome.flow.md

Green-tick output:

▸ flows/welcome.flow.md ✓ waitForIdle 420ms (animations + network idle) ✓ assert "Welcome" 38ms (testID strategy) ✓ visualSnapshot "welcome" 91ms (baseline captured) ✓ welcome — passed in 549ms (3 steps) report: .klera/reports/welcome-2026-05-01T12-04-22.json
flows/dashboard.flow.mdprose · 4 lines
flow passed · 4 / 4 steps

Iterate without re-running by hand

Add --watch and the run loop reuses the same bridge across saves:

# Terminal 1: keep the dev client + bridge running. pnpm exec klera serve # Terminal 2: watch a flow. pnpm exec klera run flows/welcome.flow.md \ --attach ws://127.0.0.1:7345 \ --watch # ✓ welcome — passed in 480ms (3 steps) # # (edit flows/welcome.flow.md, save) # ↻ flows/welcome.flow.md changed; re-running # ✓ welcome — passed in 410ms (3 steps)

Iteration latency drops from ~1.5s of process startup to ~50ms of re-parse + dispatch. Network-mock fixture JSON referenced from the flow is watched too — editing fixtures/login-response.json triggers the same re-run loop.

--watch requires --attach so the bridge stays alive across saves; mutually exclusive with --parallel. Ctrl-C exits cleanly.

See the report

Every run writes a JSON report. Render an HTML viewer with side-by-side visual diffs, idle-wait bars, the matcher trace, and (on failure) the triage card:

pnpm exec klera report .klera/reports/welcome-*.json --html report.html open report.html

For CI test-results panes, derive JUnit XML:

pnpm exec klera report .klera/reports/welcome-*.json --junit report.xml

Editor support for hand-authored YAML

The prose path doesn’t need editor schema support — prose is just markdown. Editor support kicks in for adopters who reach for the YAML escape hatch (--yaml on klera init, or hand-authored YAML flows alongside prose). The YAML scaffold ships a one-line directive that wires up tooling across every editor that runs yaml-language-server (VS Code, JetBrains, Neovim, Helix, Sublime):

# yaml-language-server: $schema=../.klera/flow.schema.json name: Welcome smoke steps: - waitForIdle: { animations: true, network: true } - assert: { visible: Welcome } - visualSnapshot: { id: welcome }

Materialise the schema once:

pnpm exec klera schema --out .klera/flow.schema.json

Re-run klera schema --out ... whenever you upgrade klera to refresh the schema for the new IR.

Troubleshooting

If klera doctor failed during init, the check that flagged the issue is the place to start. The typical first-time culprits:

  • No booted simulator. xcrun simctl boot "iPhone 16 Pro".
  • Dev client not yet rebuilt. pnpm exec expo run:ios or expo run:android.
  • Old Architecture detected. Bridgeless / Fabric is required. Check app.json for "newArchEnabled": true (default since Expo SDK 51).

If the install step failed, run it by hand with the displayed command — init’s log line shows the exact <bin> add -D <pkg…> invocation it tried.

Next steps

Last updated on