Snapvisor Docs

Migrate from Percy

Move a Percy suite to Snapvisor - how percySnapshot calls, percy exec, PERCY_TOKEN, .percy.yml widths and Storybook builds translate, and where the two capture models differ.

This guide moves an existing Percy suite to Snapvisor without rewriting your tests from scratch. You keep your test runner and your CI; what changes is the snapshot call, the token, and where baselines and review live. For a side-by-side of the two products, see Snapvisor vs Percy.

The one change that matters

Percy's SDKs do not take pictures. They serialize the page's DOM and send it to the local Percy CLI server (listening on port 5338 by default), which is why the tests run under percy exec. Percy then renders those snapshots itself.

Snapvisor captures a real screenshot in the browser your test is already running. argosScreenshot stabilizes the page and takes the picture itself, and your pipeline uploads the images. There is no local server and no wrapper command.

What this means in practice:

  • Browsers and widths are capture work you do, not rendering Snapvisor does for you. If Percy was rendering one snapshot at several widths or in several browsers, you now ask for them: viewports on each call, and one Playwright project per browser engine. We have not confirmed that there is a like-for-like replacement for Percy re-rendering a single DOM snapshot, so treat the mapping below as the closest equivalent, not an identical one.
  • Pixels will not match. The images come from a different renderer, so the first build will not resemble your Percy 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

PercySnapvisor equivalent
percySnapshot(page, name, options) (@percy/playwright)argosScreenshot(page, name, options) from @snapvisor/playwright
cy.percySnapshot(name, options) (@percy/cypress)cy.argosScreenshot(name, options) from @snapvisor/cypress
percy storybook <build dir or URL> (@percy/storybook)@snapvisor/storybook — see Storybook
percy exec -- <test command>Nothing wraps the command. Run it as you normally do; the Playwright reporter or Cypress task uploads when the run finishes.
PERCY_TOKENARGOS_TOKEN, your project token. On GitHub Actions you can skip it with tokenless authentication.
.percy.yml snapshot.widthsviewports on each argosScreenshot call — full sizes or named presets, not bare widths. There is no global widths file documented here, so keep one list in a small helper of your own.
.percy.yml snapshot.minHeightNo direct equivalent that we have confirmed. The height lives in each viewport size, and on Playwright argosScreenshot captures the full page by default.
percyCSSargosCSS — CSS applied during the screenshot process
Ignore regionsmask on Playwright (Playwright's own option, passed through). On Cypress, hide the element with argosCSS.
percy exec --parallel--parallel, --parallel-nonce, --parallel-total and --parallel-index on npx @snapvisor/cli upload — see Parallel testing.
Percy build review and approvalThe Snapvisor review UI. Approving promotes a change to the baseline for that branch — see Review workflow.
Percy DOM snapshot, rendered by PercyA screenshot taken by your test browser. See the one change that matters.

Per-snapshot options not listed here (enableJavaScript, scope, 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.

Playwright

Install the SDK and register the reporter, which uploads screenshots when the run finishes:

npm install --save-dev @snapvisor/playwright
// 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 swap the snapshot call. Percy's Playwright package is imported with require in its documentation; the diff shows that form:

 // tests/visual.spec.ts
 import { test } from "@playwright/test";
-const percySnapshot = require("@percy/playwright");
+import { argosScreenshot } from "@snapvisor/playwright";

 test("home", async ({ page }) => {
   await page.goto("http://localhost:3000/");
-  await percySnapshot(page, "Home", { widths: [375, 1280] });
+  await argosScreenshot(page, "Home", {
+    viewports: [
+      { width: 375, height: 812 },
+      { width: 1280, height: 800 },
+    ],
+  });
 });

viewports takes complete viewport sizes, not the bare widths Percy's widths accepts. You can also name a preset, for example viewports: ["iphone-6", { width: 1280, height: 800 }].

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. See Flaky test detection for how recurring changes are handled.

Cypress

npm install --save-dev @snapvisor/cypress

Register the Snapvisor task in your Cypress config, and import the command in your support file:

 // cypress.config.js
 const { defineConfig } = require("cypress");
+const { registerArgosTask } = require("@snapvisor/cypress/task");

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

Then swap the command in your tests:

 describe("Homepage", () => {
   it("renders", () => {
     cy.visit("http://localhost:3000");
-    cy.percySnapshot("Homepage test", { widths: [768, 992, 1200] });
+    cy.viewport(768, 1024);
+    cy.argosScreenshot("homepage-768");
+    cy.viewport(1200, 900);
+    cy.argosScreenshot("homepage-1200");
   });
 });

Resizing with cy.viewport() between calls is plain Cypress, and it is the same pattern Percy's own responsive-snapshot docs show for a single width at a time. cy.argosScreenshot also accepts a viewports option; check the Cypress SDK reference for its exact shape before relying on it.

Storybook

Percy snapshots a Storybook build (percy storybook ./storybook-build, or a URL) without running your tests. @snapvisor/storybook instead 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);
  },
};

Percy's per-story parameters.percy settings (names, additional snapshots, widths) 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.

The CI change

Before — the whole run is wrapped by percy exec:

- run: npm run build
- run: npx percy exec -- npx playwright test
  env:
    PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}

After — no wrapper, a different token:

- run: npm run build
- run: npx playwright test # captures via argosScreenshot, uploads via the reporter
  env:
    ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}

For Cypress, percy exec -- cypress run becomes npx cypress run. 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.

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

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.

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.

Cleaning up

Once builds are landing in Snapvisor:

  1. Remove @percy/cli and the @percy/* SDK packages (@percy/playwright, @percy/cypress, @percy/storybook) from devDependencies.
  2. Delete .percy.yml and replace each percySnapshot call with its Snapvisor equivalent.
  3. Remove percy exec from your CI scripts and PERCY_TOKEN from your CI secrets.
  4. Take any Percy status check out of the required checks in branch protection so it stops blocking merges, and require the Snapvisor check instead.

For plans and what each includes, see Pricing.

Frequently asked questions

Can I import my Percy 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 a wrapper command like percy exec?
No. Snapvisor does not wrap your test command. The Playwright reporter and the Cypress task upload the screenshots when the run finishes, authenticated by ARGOS_TOKEN, and the CLI can upload any folder of images with npx @snapvisor/cli upload.
Does Snapvisor re-render a snapshot in other browsers the way Percy does?
Do not assume so. Snapvisor compares screenshots taken by the browser your own test runs in. To cover Chromium, Firefox and WebKit, run those as Playwright projects, and capture each viewport you care about with the viewports option. Percy documents its own rendering on its own pages; this guide does not describe it beyond what is cited there.
Can I run Percy and Snapvisor side by side while I switch?
The SDKs are independent packages, so you can add Snapvisor capture calls on a branch and compare the results before removing Percy. Each tool keeps its own baselines, so expect pixel-level differences between them rather than a one-to-one match.

On this page