Snapvisor Docs

Migrate from Chromatic

Move a Chromatic suite to Snapvisor - how the chromatic CLI, CHROMATIC_PROJECT_TOKEN, TurboSnap, Storybook stories and the Playwright and Cypress packages translate, and where the two capture models differ.

This guide moves an existing Chromatic suite to Snapvisor. You keep your Storybook, your test runner and your CI. What changes is how screenshots are taken, the token, and where baselines and review live. For a side-by-side of the two products, see Snapvisor vs Chromatic.

The one change that matters

Chromatic's CLI builds your Storybook and uploads it to Chromatic's cloud. Chromatic then loads each story, or each captured test archive, in its own cloud browsers and takes the snapshots there.

Snapvisor takes a real screenshot in the browser your own test run is already using. argosScreenshot stabilizes the page and takes the picture, and your pipeline uploads the images. There is no published Storybook to host and no cloud rendering step.

What this means in practice:

  • Browsers and viewports are capture work you do. Chromatic lets you enable more browsers and viewports on its side. With Snapvisor you ask for them in your tests: viewports on each call, and one Playwright project per browser engine. We have not confirmed a like-for-like replacement for Chromatic's browser setting, so treat the mapping below as the closest equivalent.
  • Pixels will not match. The images come from a different renderer, so the first build will not resemble your Chromatic baselines pixel for pixel. That is expected.
  • Your baselines do not carry over. Snapvisor stores baselines server-side, per reference branch, and there is no import path. Your first Snapvisor build on your base branch becomes the new baseline. See Baseline builds.

Concept mapping

ChromaticSnapvisor equivalent
ProjectA Snapvisor project, with its own project token.
BuildA build: one upload of screenshots for a commit.
Snapshot of a story in a mode (a browser and viewport)A screenshot with a name, taken by your test run. Several viewports are several viewports entries.
Baseline, the last accepted snapshot on a branchThe baseline: the approved screenshot on the reference branch. See Baseline builds.
Accept or deny a changeApprove or reject a change in the review UI. Approving promotes it to the baseline for that branch. See Review workflow.
npx chromatic (the chromatic package)Nothing wraps the command. Run your test command as you do today; the plugin or reporter uploads when the run finishes.
CHROMATIC_PROJECT_TOKENARGOS_TOKEN, your project token. On GitHub Actions you can skip it with tokenless authentication.
Storybook stories published to Chromatic@snapvisor/storybook — see Storybook.
@chromatic-com/playwright@snapvisor/playwright — see Playwright.
@chromatic-com/cypress@snapvisor/cypress — see Cypress.
TurboSnap (onlyChanged)No equivalent that we have confirmed. See TurboSnap.
ignoreSelectors, data-chromatic="ignore", chromatic-ignoremask on Playwright (Playwright's own option, passed through). On Cypress, hide the element with argosCSS.
diffThresholdthreshold, but the units differ — see Thresholds.
Chromatic pull request checkThe Snapvisor commit status check — see Make the Snapvisor check the gate.

Chromatic options not listed here (delay, pauseAnimationAtEnd, media feature settings, and others) have no mapping in this guide because we have not confirmed one. Check the SDK references before assuming a Snapvisor option of the same name exists.

Storybook

Chromatic is built around Storybook, so this is the path most Chromatic projects take. @snapvisor/storybook runs your stories in a real browser and captures one screenshot per story, so the story list stays in Storybook and nothing is duplicated in a visual-testing config.

npm install --save-dev @snapvisor/storybook

The recommended route is the Storybook Vitest addon. Register the plugin in your Vitest config:

// vitest.config.ts
import { defineConfig } from "vitest/config";
import { argosVitestPlugin } from "@snapvisor/storybook/vitest-plugin";

export default defineConfig({
  plugins: [
    argosVitestPlugin({
      // Upload the screenshots only on CI.
      uploadToArgos: process.env.CI === "true",
    }),
  ],
});

A screenshot is captured for every story automatically. To capture extra screenshots at different steps of an interaction, call argosScreenshot from @snapvisor/storybook/vitest inside a story's play function.

If you run stories with the Storybook Test Runner instead:

// .storybook/test-runner.ts
import { argosScreenshot } from "@snapvisor/storybook/test-runner";

export default {
  async postVisit(page, context) {
    await argosScreenshot(page, context);
  },
};

Chromatic's per-story parameters.chromatic settings (ignoreSelectors, diffThreshold, delay and others) have no Snapvisor equivalent that we have confirmed. If you used them to snapshot a story in a second state, give that state its own story, or take an extra argosScreenshot in the story's play function. See SDKs for the package list.

TurboSnap

Chromatic documents TurboSnap as a way to skip snapshots for stories that code changes did not affect. It is switched on with --only-changed on the CLI or onlyChanged: true on the GitHub Action, and it needs your full git history.

We have not confirmed a Snapvisor feature that does the same. Snapvisor screenshots every story your test run visits. Before you remove Chromatic, time your Storybook test run in CI. If it is too slow for pull requests and your Storybook test runner supports sharding, you can split it across machines and review the shards as one build; see Parallel testing.

Playwright

If you used Chromatic's Playwright package, you replaced the @playwright/test import with @chromatic-com/playwright and ran npx chromatic --playwright against the saved test archives. Snapvisor needs one reporter and one function call.

npm install --save-dev @snapvisor/playwright

Register the reporter, which uploads screenshots when the run finishes:

// playwright.config.ts
import { defineConfig } from "@playwright/test";
import { createArgosReporterOptions } from "@snapvisor/playwright/reporter";

export default defineConfig({
  reporter: [
    process.env.CI ? ["dot"] : ["list"],
    [
      "@snapvisor/playwright/reporter",
      createArgosReporterOptions({ uploadToArgos: !!process.env.CI }),
    ],
  ],
});

Then change the import back to Playwright's own, and capture explicitly:

 // tests/visual.spec.ts
-import { test, expect } from "@chromatic-com/playwright";
+import { test, expect } from "@playwright/test";
+import { argosScreenshot } from "@snapvisor/playwright";

 test("home", async ({ page }) => {
   await page.goto("http://localhost:3000/");
   await expect(page).toHaveTitle("Home");
+  await argosScreenshot(page, "home");
 });

Chromatic's package captures an archive for each test on its own. Snapvisor captures only where you call argosScreenshot, so add a call at each state you want compared.

argosScreenshot stabilizes the page before capturing. It waits for web fonts, images and aria-busy regions to settle, hides carets and scrollbars and normalizes font anti-aliasing. It is on by default; beforeScreenshot runs your own code at the right moment if a particular page needs more. To capture several sizes, pass viewports:

await argosScreenshot(page, "home", {
  viewports: [
    { width: 375, height: 812 },
    { width: 1280, height: 800 },
  ],
});

viewports takes complete viewport sizes. You can also name a preset, for example viewports: ["iphone-6", { width: 1280, height: 800 }]. For more than one browser engine, add Playwright projects. See Flaky test detection for how recurring changes are handled.

Cypress

If you used Chromatic's Cypress package, you imported @chromatic-com/cypress/support in your support file, called installPlugin in your config, and ran npx chromatic --cypress.

npm install --save-dev @snapvisor/cypress
 // cypress.config.js
 const { defineConfig } = require("cypress");
-const { installPlugin } = require("@chromatic-com/cypress");
+const { registerArgosTask } = require("@snapvisor/cypress/task");

 module.exports = defineConfig({
   e2e: {
     setupNodeEvents(on, config) {
-      installPlugin(on, config);
+      registerArgosTask(on, config, {
+        // Upload the screenshots only on CI.
+        uploadToArgos: !!process.env.CI,
+      });
       return config;
     },
   },
 });
 // cypress/support/e2e.js
-import "@chromatic-com/cypress/support";
+import "@snapvisor/cypress/support";

Then add a capture where you want a comparison:

 describe("Homepage", () => {
   it("renders", () => {
     cy.visit("http://localhost:3000");
+    cy.argosScreenshot("homepage");
   });
 });

cy.argosScreenshot also accepts a viewports option; check the Cypress guide for its exact shape before relying on it.

Thresholds: do not copy the number over

Both tools have a sensitivity setting for diffs, and the numbers are not interchangeable.

  • Chromatic: diffThreshold, set per story, per project or in your test configuration.
  • Snapvisor: threshold, a sensitivity between 0 and 1, defaulting to 0.5. Higher means less sensitive.

Start from the Snapvisor default and adjust only if a specific screenshot is noisy:

await argosScreenshot(page, "chart", { threshold: 0.7 });

The threshold is stored with each screenshot and applied by the same engine that produces the diff you review, so what you set is what gates the build.

The CI change

Chromatic's documentation recommends a full git history for its GitHub Action (fetch-depth: 0), and TurboSnap needs it, so the usual workflow checks out that way. With the main Snapvisor GitHub App connected, Snapvisor finds the merge base on its side, so the default checkout depth is enough. See GitHub Actions recipes for the exact rules.

Before — Chromatic publishes the Storybook and runs the tests in its cloud:

- uses: actions/checkout@v6
  with:
    fetch-depth: 0
- run: npm ci
- uses: chromaui/action@latest
  with:
    projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

After — your Storybook test run takes and uploads the screenshots:

- uses: actions/checkout@v6
- run: npm ci
- run: npm run test-storybook # your existing Storybook Vitest or Test Runner command
  env:
    ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}

Copy the project token from your project's settings in the app, and store it as a CI secret — never commit it. See Access tokens. On GitHub Actions you can drop the secret entirely with tokenless authentication. More workflows, including merge queues and fork pull requests, are in GitHub Actions recipes.

If you would rather not change how screenshots are produced, write them to a folder and upload it with the CLI:

export ARGOS_TOKEN="<your project token>"
npx @snapvisor/cli upload ./screenshots

Run both side by side

You do not have to switch in one pull request. Keep the Chromatic step and add the Snapvisor step next to it:

jobs:
  chromatic:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - run: npm ci
      - uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
  snapvisor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: npm ci
      - run: npm run test-storybook # your existing Storybook Vitest or Test Runner command
        env:
          ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}

A way to run the overlap:

  1. Add the Snapvisor job on a branch, and merge it to your base branch so the first Snapvisor build becomes the baseline.
  2. Leave the Chromatic check required and the Snapvisor check optional while you compare results.
  3. When the Snapvisor builds match your expectations on a few pull requests, make the Snapvisor check required and remove the Chromatic one.

Your first build becomes the baseline

The first Snapvisor build on your base branch has nothing to compare against, so it becomes the baseline and is approved automatically. On later builds, Snapvisor diffs each screenshot against the approved baseline for the base branch and posts a status check to the pull request. Approving a change in the review UI promotes it to the new baseline.

Because the first build is approved without review, run it on a base branch that is visually correct. See Baseline builds for how the reference build and base branch are resolved.

Make the Snapvisor check the gate

upload exits 0 once the build has been created, whether or not it contains changes. A non-zero exit means the upload itself failed (bad token, unresolvable commit, network, missing parallel arguments). The review gate is the commit status check Snapvisor posts to the pull request, which asks for review until someone approves or rejects the changes.

Chromatic's CLI also exits 0 when it finds changes by default, and its documentation describes an exitZeroOnChanges: false option for making the step fail. Snapvisor has no such option. The status check is the gate.

If the check is optional, nothing blocks a merge. A green CI job after the Snapvisor upload means the screenshots arrived, not that they are unchanged. Require the check in branch protection — see GitHub or GitLab.

First-build checklist

After the first Snapvisor build on your base branch:

  1. Open the build and confirm it has a screenshot for each story and test you expected. Compare the count with your Chromatic build.
  2. Look through the screenshots for fonts, images and animations that did not settle. Fix those first, before any threshold change.
  3. Run the same build twice on one commit and check that nothing shows as changed.
  4. Open a pull request with one small visual change. Confirm the Snapvisor status check appears, asks for review, and clears after you approve.
  5. Make the Snapvisor check required in branch protection.

Cleaning up

Once builds are landing in Snapvisor and you have removed Chromatic from your pull request flow:

  1. Remove chromatic, @chromatic-com/playwright and @chromatic-com/cypress from devDependencies, and undo the installPlugin and support-file imports.
  2. Delete the chromatic script from package.json and the Chromatic workflow or step from CI.
  3. Remove CHROMATIC_PROJECT_TOKEN from your CI secrets.
  4. Take the Chromatic status checks out of the required checks in branch protection so they stop blocking merges, and require the Snapvisor check instead.

Frequently asked questions

Can I import my Chromatic baselines into Snapvisor?
No. There is no import path for another tool's baselines. Your first Snapvisor build on your base branch becomes the new baseline: it has nothing to compare against, so it is approved automatically. Plan the switch for a moment when your base branch is visually correct.
Do I still need the chromatic command?
No. Snapvisor does not build and publish your Storybook for you. Your own test run (the Storybook Vitest addon or the Storybook Test Runner) takes the screenshots, and the Snapvisor plugin uploads them when the run finishes, authenticated by ARGOS_TOKEN. The CLI can also upload any folder of images with npx @snapvisor/cli upload.
Does Snapvisor have something like TurboSnap?
We have not confirmed an equivalent. This guide does not describe one. Snapvisor captures a screenshot for every story your test run visits, so if you rely on TurboSnap to skip stories, check how long your Storybook test run takes before you switch.
Can I run Chromatic and Snapvisor side by side while I switch?
Yes. The two use different packages, tokens and CI steps, so you can add Snapvisor to a branch and compare the results before removing Chromatic. Each tool keeps its own baselines, so expect pixel-level differences between them rather than a one-to-one match.

On this page