How Chromatic Detects Visual Changes in Storybook
Chromatic renders Storybook stories in cloud browsers, compares screenshots with matching baselines, and flags differences for review. Here’s how capture, thresholds, and baselines work.
Chromatic detects visual changes by rendering Storybook stories in cloud browsers, capturing screenshots of their rendered states, and comparing each new snapshot with the appropriate accepted baseline. A visual difference is a prompt for review: accept it if the change is intentional, or fix it if it is a regression.
A story is the test case. Its args, decorators, and interaction setup define a repeatable UI state that Chromatic can render. This lets visual tests cover component states such as loading, error, selected, or disabled without manually navigating through the whole application. Chromatic’s visual testing documentation describes the process as capturing snapshots of tests in a cloud browser environment.
1. The detection process
- Define states as stories. Each story describes a component or screen in a state worth checking.
- Build and upload Storybook. Chromatic receives the Storybook build and test metadata, typically from a local command or CI.
- Render each test in a cloud browser. The test is loaded at its configured browser and viewport. Chromatic waits for the UI to render and for Storybook interaction tests to finish.
- Capture a snapshot. The resulting UI image is associated with the test and its browser, viewport, and build context.
- Compare against the matching baseline. Chromatic identifies visual differences and highlights them for review.
- Review and resolve. Accept an intentional update to advance the baseline, or change the UI/test setup and run again to resolve an unintended difference.
A baseline is the last accepted visual state for that test context. The first run establishes the starting point; later builds are compared with the relevant baseline in the branch’s build history. See Chromatic’s snapshot documentation for capture details.
2. What exactly is compared?
For a visual snapshot, Chromatic compares rendered images at corresponding coordinates and highlights the resulting differences. A change can be a real UI change—such as a spacing, color, or font update—or capture noise caused by unstable rendering. The diff itself does not determine whether the change is a defect.
The comparison is contextual. A Chrome snapshot is compared with the Chrome baseline for that test, rather than with a Firefox screenshot. Browser and viewport variants can have their own baselines. This avoids treating ordinary rendering differences between browser environments as changes to one shared image. Adding browser or viewport coverage therefore increases both the coverage and the number of snapshots to review. See Chromatic’s browser documentation.
3. Thresholds and sensitivity
The diffThreshold setting controls how much color difference Chromatic tolerates before considering a pixel changed. Its documented default is .063. The comparison uses color distance in YIQ space at corresponding image coordinates; the threshold is not the percentage of the page that must change.
- Lower threshold: more sensitive to subtle changes, with a greater chance of flagging rendering noise.
- Higher threshold: less sensitive, reducing small noisy diffs but potentially overlooking subtle intended-to-be-detected changes.
Anti-aliased pixels are ignored by default. If you need them included, use the documented configuration option for your test setup. Tune thresholds against representative stories and changes rather than treating one value as universally correct. The threshold guide explains the option and its tradeoffs.
// Example for a Chromatic UI test configuration:
export default {
// Lower values are more sensitive; use the documented range and
// configuration location for your test runner/version.
diffThreshold: 0.03,
};
This illustrates the setting only; configuration placement differs among Storybook, Chromatic UI Tests, and test-runner integrations. Follow the matching documentation for the integration and version in your project. Do not set a very low threshold until capture noise is controlled.
4. Set up and run the Storybook workflow
Run from the Visual Tests addon
- Use Storybook 7.6 or later and install the official addon using
npx storybook@latest add @chromatic-com/storybook. - Start Storybook, open the Visual Tests panel, and connect it to a Chromatic project. The setup configures the project identifier.
- Run visual tests from the panel. Inspect highlighted stories and their captured snapshots.
- Accept intentional changes as baselines, or fix unintended changes and rerun.
The addon supports project configuration in chromatic.config.json. Documented options include projectId, buildScriptName for a custom Storybook build command, debug for verbose output, and zip for deploying a large Storybook as a zip. Consult the Storybook visual testing guide for current setup details.
Run from a terminal or CI
Install the Chromatic CLI as a development dependency, then run it with the project token from your Chromatic project:
npm install --save-dev chromatic
npx chromatic --project-token=YOUR_CHROMATIC_PROJECT_TOKEN
In CI, provide the token through the CI platform’s secret/environment-variable mechanism, build Storybook, and run the CLI as a job step. Keep the token out of committed source and logs. The first successful run establishes baselines; later builds compare snapshots to the appropriate accepted baseline. The exact CI configuration depends on the provider and package manager. Chromatic’s documentation covers CLI and workflow options.
5. Make captures repeatable
Reliable comparison depends on capturing the same intended state on each run. Chromatic estimates readiness using rendering and network activity, and waits for Storybook interaction play functions. CSS animations, transitions, videos, and GIFs are paused to reduce noise, but JavaScript-driven animation and other time-dependent behavior may still vary.
- Use fixed story args and deterministic fixtures rather than live or changing data.
- Set a predictable clock, random seed, and animation state in the story or test where those affect the rendered UI.
- Wait for asynchronous content explicitly in interaction tests; avoid arbitrary sleeps when a specific state can be awaited.
- Use stable image assets and make sure fonts and other resources load before capture.
- Keep viewport and browser configuration consistent between runs.
- For animated content, render a stable frame or disable the JavaScript animation in the test environment.
Longer waits can improve completeness when the UI genuinely needs time, but they also make runs slower and do not fix nondeterministic state. Chromatic’s snapshot guide describes its capture sequence and animation caveats.
6. Troubleshooting common visual diffs
| Symptom | Likely cause | What to do |
|---|---|---|
| Many unrelated stories change at once | A shared style, font, dependency, or browser-rendering input changed; alternatively, a resource failed to load. | Inspect shared components and global styles first. Check the captured snapshot and build output for missing assets or changed dependencies. |
| Diffs change from run to run | Time, random data, network-dependent content, or JavaScript animation is nondeterministic. | Use fixed fixtures and a stable clock/seed; disable or control animation; wait for the intended state. |
| A visible subtle color update is not flagged | The difference falls within the configured color threshold. | Lower diffThreshold carefully and review representative captures. Remember it measures color tolerance, not changed-area percentage. |
| There are noisy edges around text or shapes | Anti-aliasing or rendering variation can affect edge pixels. | Keep the default anti-aliasing handling unless pixel-level edge differences matter; first check that browser and viewport context are stable. |
| A story is captured before content appears | The UI was not ready when capture began, or the expected network/rendering signal was not sufficient. | Wait for a specific selector or state in the story’s interaction test, and ensure the asynchronous operation resolves in the test environment. |
| The snapshot differs from the local Canvas view | The cloud capture environment, viewport, fonts, loaded resources, or timing differs from the local preview. | Inspect the Chromatic snapshot and compare environment inputs. Make story state and resources deterministic before accepting a new baseline. |
| A baseline update seems to affect only some results | Baselines are scoped by test and may vary by browser, viewport, and branch ancestry. | Check which test context is shown and whether the accepted build is in the relevant branch history. |
7. Performance, reliability, and cost considerations
Story count, browser variants, viewport variants, and capture time all affect the amount of work in a visual test run. Keep stories focused on meaningful states, and add browser or viewport variants where they provide coverage your product needs. Chromatic documents TurboSnap as a way to avoid unnecessary snapshot work when it determines related code has not changed; check the current documentation and plan terms for availability and billing details.
For reliability, make visual tests part of a repeatable build, preserve the same dependency lockfile and build inputs, and review diffs before updating baselines. A baseline should represent an intentional, reviewed state, not a convenient way to clear noisy output. Visual diffs are complementary to interaction, accessibility, and functional tests: matching screenshots cannot prove that every behavior works.
Costs depend on the Chromatic plan and usage model; snapshot volume can grow with added tests and browser or viewport variants. Check current plan limits and billing documentation before expanding coverage. This article does not assume a price or snapshot allowance.
8. Chromatic visual tests versus a standalone screenshot
Chromatic answers a versioned regression-testing question: “Did this Storybook test render differently from its accepted baseline in this browser and viewport context?” A general screenshot API answers a capture question: “Can I request an image or PDF of this URL or rendered content?” A standalone capture can help inspect or document a page, but it does not automatically provide Storybook test baselines, branch review, or visual regression decisions.
Or skip the browser setup
If you need a screenshot of a live page without configuring a browser automation stack, ScreenshotNeo is a website screenshot API and MCP server. It does not replace Chromatic’s story-based baseline workflow; it is useful when the task is to capture a URL as an image or PDF.
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. Cookie banners are accepted and removed before capture, along with known newsletter popups and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo and get 1,000 free screenshots a month, with no card.
FAQ
Does every detected change mean a bug?
No. Chromatic reports a visual difference; a developer decides whether it is an intended update or a regression.
Does Chromatic compare Chrome directly with Firefox?
No. Each browser’s capture is compared with its corresponding baseline.
Is diffThreshold: 0.063 a 6.3% page-change limit?
No. It is a tolerance for color distance at image coordinates, not a percentage of pixels or page area.
Can Chromatic make JavaScript animations deterministic automatically?
Not necessarily. Control JavaScript-driven animation and other nondeterministic behavior in the test setup.


