How to Speed Up Visual Regression Tests with TurboSnap
Learn how TurboSnap selects affected stories, how to enable it safely, and how to diagnose broad retests, snapshot costs, and CI slowdowns.
TurboSnap speeds up Chromatic UI Tests by using Git history and a Webpack or Vite dependency graph to identify which Storybook stories may be affected by a change. It captures fresh snapshots for affected stories and reuses snapshots for unaffected ones. This can reduce test work and billed snapshot usage; the actual runtime improvement depends on your project, changes, build, and CI environment.
TurboSnap is not a generic browser-testing switch that simply filters the pull request’s changed-file list. Its results depend on build ancestry and dependency tracking. Enable it only after confirming compatibility, then inspect each build’s output to verify what it tested.
1. How TurboSnap works
Chromatic compares the current build with an ancestor build in its build history. It uses Git changes together with the bundler’s dependency graph to determine which story files might be affected. A story with a changed dependency gets a new snapshot; a story with no associated code changes can reuse its prior snapshot.
- Chromatic identifies the relevant ancestor build and the changes since that build.
- TurboSnap follows dependencies from changed files to story files.
- It captures affected stories and reuses snapshots for stories it considers unaffected.
- The CLI reports the files traversed, affected stories, and stories tested or captured. Review these details to confirm that selective testing is active.
Because the comparison point is an ancestor Chromatic build, the analyzed change set can differ from the current pull request diff. If the ancestor is older than the latest base-branch build, TurboSnap can analyze files that are not in the PR’s current diff.
2. Check prerequisites before enabling it
Chromatic’s documented Storybook setup lists these requirements. They are version-sensitive, so check the current TurboSnap setup guide before changing CI configuration.
| Requirement | What to check |
|---|---|
| Chromatic CLI | Version 10.0 or later for the documented setup. |
| Storybook or Vitest | Storybook 6.5 or later, or Vitest 4 or later. |
| Git | Version 2.28.0 or later. |
| Bundler | A Webpack or Vite project whose dependency information Chromatic can use. |
| Stories and UI Tests | Stories are correctly configured, and Chromatic UI Tests are enabled. |
| Build history | For the documented Storybook flow, Chromatic lists ten successful CI builds as a prerequisite. |
| GitHub Actions trigger | The setup guide says to run the workflow on push, rather than pull_request. |
First learn how your ordinary Chromatic build behaves. TurboSnap adds dependency-analysis complexity, and a tracking gap can make a visual change harder to diagnose. Establish a baseline build and confirm stories are captured as expected before comparing results.
3. Enable TurboSnap
You can enable it on the CLI with --only-changed, or set onlyChanged: true in the supported GitHub Action or Chromatic configuration. Follow the current official guide for the exact workflow syntax for your installed action and CLI version.
CLI invocation
npx chromatic --only-changed
Use this with the same project token and build setup as your existing Chromatic command. Do not create a second, differently configured Storybook build just to turn on the flag.
GitHub Action configuration
In the GitHub Action configuration, set onlyChanged: true using the input syntax supported by the version you have installed, and trigger the workflow on push as the setup guide specifies. Confirm the workflow is building the intended Storybook project, particularly in a monorepo.
Chromatic configuration
If your supported Chromatic configuration file is the source of settings, set onlyChanged: true there. Keep the setting consistent with the command and CI workflow so local and CI builds do not silently use different behavior.
After enabling, inspect the first several build logs. Look for the count of changed files traversed, affected story files, and stories tested or snapshots captured. A successful command alone does not prove TurboSnap selected the stories you expect.
4. Make dependency tracking reliable
TurboSnap can only make a sound selection when the build’s inputs are represented and the comparison build is appropriate. Use this checklist when adopting it:
- Keep the lockfile present and in sync. Chromatic says a missing or out-of-sync lockfile may lead it to retest all stories.
- Review inputs outside the bundler graph. Static assets and files such as Sass or templates can affect a story even when they are not represented as ordinary imported modules. Check the setup guide for how to account for them.
- Generate stats for prebuilt Storybooks when required. The guide describes generating stats JSON for prebuilt builds so dependency analysis has the needed information.
- Check project paths in monorepos. Ensure Chromatic resolves the correct Storybook project and its dependencies.
- Inspect dynamic imports and shared preview dependencies. TurboSnap’s helper analysis can reveal imports or shared preview code that cause small changes to affect many stories.
- Use a suitable build ancestor. Rebase onto the latest base branch when you want the comparison to align more closely with the current PR diff.
5. Understand snapshot usage and runtime
Snapshot reuse affects billed usage, but a reduction in billed snapshots is not the same thing as a guaranteed reduction in wall-clock CI time. The build still has setup and analysis work, and actual timing depends on your project and CI.
| Snapshot outcome | Chromatic billing weight |
|---|---|
| Freshly captured snapshot | 1 billed snapshot |
| Copied snapshot | 0.2 billed snapshot |
| Bypassed snapshot | 0 billed snapshots, when eligible |
For example, Chromatic’s documentation illustrates 50 stories with 10 impacted stories and 40 copied snapshots: 10 fresh captures cost 10 billed snapshots, while 40 copied snapshots cost 8, for 18 billed snapshots total. This is a billing example, not a universal runtime result.
Bypassing snapshots has additional requirements: Chromatic CLI 17.7.0 or later, no changed story dependency, exactly one ancestor build, an eligible ancestor build state or the same branch, and a build that is not from the local Visual Tests Addon. Consult the official TurboSnap documentation for current eligibility details.
Chromatic’s product page claims TurboSnap can reduce usage costs by up to 80%. That is Chromatic’s promotional claim; it is not an independently verified benchmark or a promise of equivalent CI runtime improvement.
6. Diagnose broad retests and unexpected files
| Symptom | Likely cause | What to do |
|---|---|---|
| TurboSnap tests every story | The lockfile is missing or inconsistent, the dependency graph is unavailable, or Chromatic cannot safely determine the affected set. | Check lockfile consistency, Storybook build output, stats generation for prebuilt builds, project path, and CLI diagnostics. Review the current setup guide for the project type. |
| A small change affects many stories | The changed file is shared, is imported by preview configuration, or sits on a dependency path to many stories. Dynamic imports can also broaden analysis. | Inspect the helper analysis and imports. Broad retesting may be correct when shared code can alter many stories. |
| Files outside the PR diff are analyzed | TurboSnap compares against an ancestor build in Chromatic history, which may predate the latest base branch. | Check the selected ancestor build and rebase onto the latest base branch if a closer comparison is needed. |
| A stylesheet, template, or asset change seems missed | The input may not be represented in the bundler dependency tree used for story selection. | Review static-file and external-input configuration in the setup guide; verify that the file is connected to the relevant stories. |
| The action behaves differently from local CLI runs | Different versions, configuration sources, project directories, or build triggers can change the analysis. | Align CLI and action settings, confirm the working directory and Storybook path, and compare logs from the same commit. |
| Snapshot usage falls but CI duration does not | Snapshot billing and wall-clock time measure different things; analysis and build setup still take time. | Compare repeated CI runs on your own project, recording build duration and billed usage separately. |
7. Measure whether it helps your project
- Record a baseline using your normal Chromatic workflow: CI duration, stories captured, and billed snapshot usage.
- Enable TurboSnap on a branch or workflow where you can inspect the full build logs.
- For each run, note the changed files traversed, affected story count, captured story count, total build duration, and snapshot usage.
- Compare similar changes and CI conditions. A broad shared dependency change is not comparable to a small leaf-component change.
- Keep the configuration only if selection is reliable for your project and the measured tradeoff suits your team.
Do not infer a runtime gain from the number of copied snapshots alone. Chromatic documents the selection and billing mechanics, but the reviewed sources do not provide a neutral head-to-head runtime benchmark.
8. Or skip the browser setup
If you need screenshots of live pages for documentation, QA, or another workflow alongside visual regression testing, ScreenshotNeo is a website screenshot API and MCP server. It does not replace TurboSnap’s Storybook dependency analysis; it provides a one-request way to capture a URL as an image or PDF.
Make a GET request with a URL. For example, using cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for parameters and response details. The equivalent Python request is:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
And with Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
- Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
- Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses include
X-Page-VerdictandX-Billedheaders. - An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.
Sign up free for 1,000 screenshots a month, with no card required.
9. Frequently asked questions
Does TurboSnap change which stories exist in Storybook?
No. It selects which stories need fresh visual snapshots for a build; it does not remove stories from your Storybook.
Can I treat TurboSnap’s result as the pull request’s exact changed-file list?
No. It uses an ancestor Chromatic build and dependency analysis, so its analyzed files can differ from the current PR diff.
Does fewer billed snapshots mean the build is proportionally faster?
No. Billing weights describe snapshot usage. Build setup, dependency analysis, and CI conditions also affect elapsed time.
Where should I look first if the selection surprises me?
Start with the CLI output for traversed files and affected stories, then verify the lockfile, dependency inputs, Storybook path, and ancestor build.


