How Squiggle works
For AI agents: this page is Markdown at https://squiggle.sh/docs/how-it-works.md. Every docs page is listed at https://squiggle.sh/llms.txt, and all of them are one file at https://squiggle.sh/llms-full.txt.
Squiggle has a part in your app and a part on your Mac. When you write tests, one rule matters most: a step that passes was sent, which is not the same as working.
The SDK, in your app
The SDK is the @squiggle.sh/react-native package. While your app runs, it keeps a live model of it: every component on screen, what rendered and why, store changes, network requests, the current screen, and what each tap set off. The docs call this model the runtime graph.
Everything else in Squiggle reads that model. A flow finds the Add to cart button in it, Studio draws it, and a performance finding points at a component in it. If the SDK isn’t in your app build, Squiggle has nothing to read, so setup starts there.
The SDK only records while something on your Mac is connected to it. With nothing connected, it stays idle and costs close to nothing.
Studio and the CLI, on your Mac
Studio is the desktop app. It shows your simulator, the Inspector, performance and analytics, and it records and replays flows. The CLI is the squiggle command. It runs flows in your terminal and in CI, and it includes the MCP server that lets a coding agent drive your app.
Both talk to the same app, and both keep their files in one .squiggle/ folder at your project root, so a flow recorded in Studio runs from the terminal unchanged. The Project folder page lists what’s in it and what to commit.
To drive the simulator quickly, Squiggle needs your app launched with Squiggle attached. Studio and squiggle up launch it that way. If you launch the app from Xcode instead, flows still run, only more slowly. Troubleshooting explains how to tell.
A passing step was sent, not proven
When a flow taps a button, the step passes once the tap reaches the button. It doesn’t check that the app reacted. If the button is broken, the tap still passes, and Squiggle prints a warning when nothing in the app changed afterwards.
To check that something happened, add an assert after the action. An assert looks at the screen again and fails when the result isn’t there. Try both versions of the same flow against a broken Add to cart button:
- tap: { testID: add-to-cart }- taptestID=add-to-cart — ⚠ no graph reaction (retried)
- ✔ PASS 1 of 1 steps
The tap reached the button, so the step passes. Nothing checked that the cart changed.
End every journey with an assert about what someone using your app should see. The flows page covers the kinds of assert you can write.