Skip to content

Migrate from Chromatic

A step-by-step guide for teams switching from Chromatic to pxdiff. Most migrations take under 10 minutes — replace the CLI, swap one env var, and your existing Storybook parameters keep working.

Sign up at pxdiff.com and create an organization and project. Then create an API key and set it as a repository secret named PXDIFF_API_KEY.

Replace the chromatic package with @pxdiff/cli:

Terminal window
npm uninstall chromatic
npm install -D @pxdiff/cli

Or install globally:

Terminal window
npm install -g @pxdiff/cli

Replace your Chromatic project token with a pxdiff API key:

env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
PXDIFF_API_KEY: ${{ secrets.PXDIFF_API_KEY }}

Replace the Chromatic action with pxdiff/storybook:

- name: Build Storybook
run: npm run build-storybook
- name: Chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: pxdiff
uses: pxdiff/storybook@v1
with:
api-key: ${{ secrets.PXDIFF_API_KEY }}
source: ./storybook-static

See Storybook Action reference for all inputs and outputs.

Replace the npx chromatic command:

- run: npm run build-storybook
- run: npx chromatic --storybook-build-dir ./storybook-static
- run: |
pxdiff storybook ./storybook-static
pxdiff diff
env:
PXDIFF_API_KEY: ${{ secrets.PXDIFF_API_KEY }}

pxdiff reads chromatic.* parameters as a fallback, so your existing story configuration keeps working without changes:

Chromatic parameterpxdiff equivalentFallback?
chromatic.delaypxdiff.delayYes
chromatic.viewportspxdiff.viewportsYes
chromatic.modespxdiff.modesYes
chromatic.disableSnapshotpxdiff.disableYes
chromatic.cropToViewportpxdiff.cropToViewportYes
chromatic.cssSelectorpxdiff.selectorYes
chromatic.ignoreSelectorspxdiff.ignoreSelectorsYes
chromatic.diffThreshold--threshold flagExtracted, applied per-job

You can migrate parameters to the pxdiff namespace at your own pace, or leave them as-is.

// These both work:
parameters: {
chromatic: { delay: 500 },
}
parameters: {
pxdiff: { delay: 500 },
}

If both namespaces are present, pxdiff takes precedence.

If you use Chromatic’s isChromatic() helper to hide dynamic content during captures, replace the import with pxdiff’s isCapturing():

import { isChromatic } from "chromatic/isChromatic";
import { isCapturing } from "@pxdiff/cli/storybook";
const showClock = !isChromatic();
const showClock = !isCapturing();

isCapturing() checks for the same chromatic=true URL parameter that pxdiff appends to story URLs during capture. It’s SSR-safe and has no dependencies.

Elements marked with data-chromatic="ignore" are hidden during captures, just like data-pxdiff="ignore":

<!-- Both attributes work -->
<div data-chromatic="ignore">dynamic content</div>
<div data-pxdiff="ignore">dynamic content</div>
ChromaticpxdiffDescription
BuildCaptureA set of screenshots taken at a point in time
TestDiffComparing a capture against baselines
AcceptApprovePromoting a screenshot to become the new baseline
BaselineBaselineThe expected screenshot for comparison
CHROMATIC_PROJECT_TOKENPXDIFF_API_KEYCI authentication
chromatic.config.json(none)pxdiff uses CLI flags and env vars only
npx chromaticpxdiff storybook + pxdiff diffCLI entry points
chromaui/actionpxdiff/storybookGitHub Action
  • Approvals and baselines — approve snapshots in the review UI, and they become baselines for future diffs. Carry-forward works automatically for snapshots not captured in the current run.
  • GitHub check runs — pxdiff creates status checks on your pull requests, just like Chromatic.
  • Pixel-level diffing — pxdiff uses the same diffing library (pixelmatch) at the same threshold (0.063) as Chromatic. Your diffs will look identical.
  • Managed browser fleet — screenshots are captured in cloud Chromium browsers, not on your CI runner. Results are deterministic across environments. Captures take a few minutes regardless of snapshot count — same as Chromatic.
  • Interaction testing — Storybook play functions run to completion before capture.
  • One workflow, not two — Chromatic splits visual testing into “Tests” (per-snapshot approvals that update baselines) and “Reviews” (all-or-nothing approvals that don’t update baselines). pxdiff has one workflow: every diff lets you approve snapshots individually, and approvals always update baselines. No mode to choose, no confusion.
  • Stacked PRs and flexible baselines — Chromatic can’t resolve baselines when an intermediate branch is missing a capture, making stacked PRs painful — you’d need to run Chromatic on every commit in the stack to keep baselines connected. pxdiff resolves baselines from the merge-base of any ref, so stacked branches work naturally without extra captures.
  • Credit-based pricing — pxdiff charges per screenshot rather than monthly tiers with overage fees. You buy credits and spend them as you go. No surprise bills.
  • No TurboSnap — pxdiff captures all stories on every run. TurboSnap-style dependency analysis is on the roadmap.
  • First-class local mode — run pxdiff storybook ./storybook-static --local to capture and diff locally without affecting team baselines. Chromatic only supports local development through a Storybook plugin — pxdiff supports it from the CLI for any integration.
  • No config file — pxdiff doesn’t use a config file. All configuration is via CLI flags and environment variables.
  • Framework-agnostic — pxdiff isn’t limited to Storybook. You can test any URL, upload any PNG, or use native Playwright and Vitest plugins.

Common Chromatic CLI flags and their pxdiff equivalents:

ChromaticpxdiffNotes
--project-tokenPXDIFF_API_KEY env varpxdiff uses an env var, not a flag
--storybook-build-dirfirst positional argpxdiff storybook ./storybook-static
--auto-accept-changes--auto-baselineSet baselines from captured screenshots (no diff)
--only-changed(not yet available)TurboSnap equivalent, on the roadmap
--skip--filter (inverted)Filter stories by glob pattern
--exit-zero-on-changes(check exit code)pxdiff exits 0 on pass, 1 on changes
--branch-name--branchOverride detected branch
--patch-build--baseCompare against a specific ref