# Chromatic docs > Chromatic is a cloud-based toolchain for visual testing, reviewing, and documenting Storybook components. Site: https://chromatic.com/docs --- ## Why Chromatic? URL: https://chromatic.com/docs > Chromatic is a cloud-based toolchain that integrates with Storybook, Playwright, and Cypress to help teams test and review UIs. # Why Chromatic? Chromatic is a testing platform that scans every possible UI state across browsers to catch bugs in appearance, functionality, and accessibility. It enables you to assign reviewers and resolve discussions to streamline team sign-off. Chromatic is made by the [team](https://www.chromatic.com/company/about) behind Storybook and integrates with Storybook, Playwright, and Cypress. ## Test how UIs look & function Every time you push code, Chromatic runs the suite of UI Tests on your code to pinpoint bugs instantly. All tests run in parallel, including testing across your chosen combinations of viewports and browsers. - [**Visual tests**](/docs/visual): Pinpoint changes in appearance, layout, fonts, and colors. - [**Interaction tests**](/docs/interactions): Simulate user actions such as clicking, typing, hovering, dragging, etc., to confirm your app behaves as expected. - [**Accessibility tests**](/docs/accessibility): run axe on each component to identify accessibility violations and get remediation tips. ![UI Tests](../../images/workflow-uitest.png) ## Benefits of testing with Chromatic Chromatic’s UI Tests lets you gain full confidence in your UIs on every commit and code change. 🎛️ **Test the real UI**: Chromatic takes pixel-perfect snapshots of real code, styling, and assets. Your tests reflect exactly what your users see. ✅ **No test flake**: Chromatic uses [SteadySnap](https://www.chromatic.com/features/steadysnap) to eliminate test flake by stabilizing frontend rendering. It tracks browser activity, freezes dynamic content, and uses burst capture to ensure consistent results every time. Tests that still render inconsistently are flagged as [unstable](/docs/flake-filter) and won't block your build. 🏎️ **Run tests in parallel by default**: Chromatic tests run on as many machines as required to deliver results in the least time possible. Parallelization is automatic with no extra configuration or cost required. 🌐 **Cross-browser testing**: Chromatic test coverage extends to Chrome, Firefox, Safari, and Edge. All browser tests run in parallel so your test suite stays fast. 📲 **Responsive viewport testing**: Chromatic allows testing at all viewport sizes. You can adjust both width and height, and configure viewports globally or for individual tests. ⚡ **Test only what's changed**: Chromatic’s [TurboSnap](https://www.chromatic.com/features/turbosnap) feature speeds up test runs and reduces usage costs by up to 80%. It works by analyzing your project’s Git history and bundler dependency graph to identify component files and dependencies with changes, then it snapshots only the tests related to those changes. 🤖 **Higher codegen quality**: [Storybook MCP](/docs/mcp) improves AI agent output by providing it with better context about your components. Chromatic simplifies the process hosting your Storybook MCP server with proper authentication and access control. ## Powered by your existing tests Chromatic's UI Tests build on the Storybook, Playwright, and Cypress tests you’ve already written. This dramatically reduces maintenance burden and setup. ### Storybook Storybook lets you capture every component state and variation as stories. Chromatic then automatically converts them into tests, catching any unexpected changes and guaranteeing a flawless UX for your users.
Why use Storybook? [Storybook](https://storybook.js.org/) is an open-source tool for developing UI components in isolation and creating living, interactive component documentation. It makes it trivial to reproduce hard-to-reach component states and ensures those states are documented in code. When you adopt Storybook, you also unlock automation for UI components and libraries via Chromatic. New to Storybook? Check out our hands-on tutorials at [storybook.js.org/tutorials](https://storybook.js.org/tutorials/). [Stories](https://storybook.js.org/docs/get-started/whats-a-story) capture all states and variations of a component. They’re a pragmatic, reproducible way to keep track of UI test cases. Using [play functions](https://storybook.js.org/docs/writing-stories/play-function), stories allow you to simulate user interactions to run functional tests on components. Chromatic uses these stories to power visual tests.
Chromatic manages the entire testing process for you. Everything from building and publishing your Storybook to running tests in cloud browsers. ![Storybook workflow: build components in Storybook and then test them with Chromatic](../../images/workflow-publish.png) [**Zero-config interaction tests:**](/docs/interactions#how-to-write-interaction-tests) If you use Storybook's [play functions](https://storybook.js.org/docs/writing-stories/play-function) for functional testing, Chromatic intelligently waits for their completion before capturing snapshots. [**Effortless variant testing:**](/docs/modes/) Chromatic harnesses the power of [Storybook Globals](https://storybook.js.org/docs/essentials/toolbars-and-globals#globals) to comprehensively test UI variations across themes, viewports, locales, and media features. ### Playwright and Cypress Run your E2E tests as usual. While those tests run, Chromatic collects a complete archive of your UI (DOM, styles, and assets). It then renders that archived UI in cloud browsers, captures visual snapshots, and identifies visual regressions through pixel diffing. ![E2E workflow: Run e2e tests, snapshot pages and then detect visual bugs with Chromatic](../../images/workflow-e2e-test.png) ## Get started Experience the power of Chromatic firsthand! Setup takes just two minutes. Pick your testing tool of choice and get started.
--- ## Quickstart URL: https://chromatic.com/docs/quickstart > Learn how to setup Chromatic for Storybook and run tests # Chromatic for Storybook Chromatic, created by the team behind Storybook, seamlessly integrates visual testing into your UI development workflow. Storybook lets you capture every component state and variation as [stories](https://storybook.js.org/docs/get-started/whats-a-story). Then, Chromatic automatically executes visual, interaction and accessibility tests on those stories.
ℹ️ Not using Storybook? Chromatic integrates with [Playwright](/docs/playwright) and [Cypress](/docs/cypress) to transform your E2E tests into visual regression tests.
## Set up Chromatic for Storybook The Chromatic CLI builds and uploads your Storybook to the Chromatic cloud infrastructure and triggers UI tests without requiring any additional configuration. ### 1. Sign up and create a new project Generate a unique project token for your app by signing in to [Chromatic](https://www.chromatic.com/start) and creating a project. Sign in with your GitHub, GitLab, Bitbucket, or email. ![Setup project](../../images/setup.png) ### 2. Install Install the [chromatic](https://www.npmjs.com/package/chromatic) package from npm. {/* prettier-ignore-start */} ```shell $ npm install --save-dev chromatic ``` ```shell $ yarn add --dev chromatic ``` ```shell $ pnpm add --save-dev chromatic ``` {/* prettier-ignore-end */}
Storybook 6.5 or later is required. We also guarantee support for officially maintained version of Node, currently 18, 20 & 21. Other Node releases may encounter errors. To switch between Node versions, we recommend using nvm,{' '} n, or another version manager.
Learn how to add chromatic to your package.json The `chromatic` command will also give you the option of adding an npm script to your `package.json` so you can run future builds with `npm run chromatic/yarn chromatic`. If you want to add it manually, it should look something like: ```json { "scripts": { "chromatic": "chromatic" } } ``` The above script command will pick up your project token by reading the `CHROMATIC_PROJECT_TOKEN` environment variable. After adding the above, ensure you set `CHROMATIC_PROJECT_TOKEN` when you run builds - such as in your CI config. If you allowed `chromatic` to add the above line, it will also have written the environment variable to your `package.json`. This environment variable can also be set via your CI config for extra privacy.
Setup .gitignore to ignore certain files in your Git repository Running the Chromatic command may generate certain files for logging and debugging purposes. Exactly which files it generates depends on your configuration, but these entries should likely be added to your `.gitignore` file: ``` build-storybook.log chromatic.log chromatic-build-*.xml chromatic-diagnostics.json ```
### 3. Run your first build to establish baselines Once you installed the `chromatic` package and have a project token, you can establish baselines by running a Chromatic build in a new project. This captures a snapshot of each test in a cloud browser and sets it as the baseline. Subsequent builds will generate new snapshots that are compared against existing baselines to detect UI changes. {/* prettier-ignore-start */} ```shell $ npx chromatic --project-token ``` ```shell $ yarn chromatic --project-token ``` ```shell $ pnpm chromatic --project-token ``` {/* prettier-ignore-end */}
We use the build-storybook script from your package.json by default. If you have customized the storybook script (for example, adding a static directory with{' '} -s), check that you've done the same for build-storybook.
### 4. Review changes On each build, Chromatic compares new snapshots to existing baselines from previous builds. Try modifying the UI a bit and running another Chromatic build. When tests are complete, you’ll see the build status and a link to review the changes. Click on that link to open Chromatic. ```shell Build 1 published. View it online at https://www.chromatic.com/build?appId=...&number=1. ``` ![Chromatic build screen with a list of stories that have visual changes](../../images/build-test-unreviewed.png) The build will be marked “unreviewed” and the changes will be listed in the “Tests” table. Go through each snapshot to review the diff and approve or reject the change. ✅ **Accept change**: This updates the story baseline, ensuring future snapshots are compared against the latest approved version. Once a snapshot is accepted, it won't need re-acceptance until it changes, even across git branches or merges. ❌ **Deny change**: This marks the change as “denied”, indicating a regression and immediately failing the build. You can deny multiple changes per build. Denying a change will force a re-capture on the next build, even if [TurboSnap](/docs/turbosnap) would otherwise skip it. ![Clicking on a story takes you to the snapshot page where you can compare the new snapshot to the baseline](../../images/snapshot-unreviewed.png) ### 5. Discussions Reviewers can create discussions to point out bugs or ask questions. Discussions are shown within the same interface as Chromatic’s detected UI changes, so all collaborators have the same reference point. Alternatively, you can click on a snapshot to create a discussion pinned to a specific change and provide precise feedback on the issue. Pair discussions with denying a change to block merging until bugs are resolved. ### 6. Merge If you accept all the changes, the build will **🟢 Pass**. Future builds whose stories have the same appearance will pass. If you deny any of the changes, the build will **🔴 Fail**. You will need to make code changes (and thus start a new build) to get the build to pass. When your build is passed (all changes accepted), you’re ready to merge with confidence, knowing that your UI is bug-free. Chromatic will update the PR check for “UI Tests” to reflect the build status. After you merge your code, Chromatic will also apply accepted baselines to stories on the target branch. That means you’ll only need to accept baselines a single time. ![Build with reviewed tests](../../images/build-test-reviewed.png) ### 7. PR check for “UI Tests” ![PR badge for UI Tests](../../images/prbadge-test.png) Chromatic adds a ‘UI Tests’ badge to the list of status checks for your pull/merge requests. The badge shows errors or changes that need to be reviewed. Require the check in [GitHub](https://help.github.com/en/github/administering-a-repository/enabling-required-status-checks), [GitLab](https://docs.gitlab.com/ee/api/commits.html#post-the-build-status-to-a-commit), or [Bitbucket](https://confluence.atlassian.com/bitbucket/suggest-or-require-checks-before-a-merge-856691474.html) to prevent accidental UI bugs from being merged. [**Integrate Chromatic into your CI pipeline**](/docs/ci) to get notified about any visual changes introduced by a pull request. Chromatic will run tests when you push code and report changes via the “UI Tests” badge for your pull request. --- ## Next: enhance your UI Testing workflow You can further refine your testing process and guarantee polished UIs in all scenarios with these strategies: - [**Test component functionality with the play function:**](/docs/interactions#how-to-write-interaction-tests) Simulate clicks, hovers, and more to simultaneously test how components look & function. - [**Test dark mode and other component variations:**](/docs/modes/) Make sure your UI adapts perfectly to different themes, locales and viewport sizes. - [**Test for flexible user preferences:**](/docs/media-features/) Guarantee your components adapt seamlessly to media features like `forced-colors` and `prefers-reduced-motion`. - [**Speed up tests & optimize usage:**](/docs/turbosnap/) Learn how to use Chromatic’s TurboSnap to only test stories that have code changes associated with them. --- ## Frequently asked questions
Can I disable UI Tests if I prefer not to use them? Yes. Go to your project's manage page, where you can disable UI Tests. Once disabled, Chromatic will no longer add status checks to your PRs for UI Tests.
Can I rerun a build without running my whole CI workflow? Yes, you can [rerun the latest build on any branch](/docs/rerun-builds) outside your CI workflow. Go to the build page to kick off a new build that uses settings and configurations identical to your old build.
What’s the difference between denied and unreviewed changes? The purpose of denying is to mark changes you’ve looked at but not accepted. After reviewing the build, the list of denied changes helps you track what needs fixing. When it comes to baselines, denying and leaving unreviewed have the same effect. In both cases, the original baseline is used for comparisons. This means in subsequent builds, Chromatic compares the latest build to the original baseline (not the previously denied snapshot). Denied changes will be marked as unreviewed in subsequent builds for you to review again.
Speed up review with keyboard shortcuts Use keyboard shortcuts to verify UI changes faster. Protip: Pressing 1 multiple times switches between the baseline and the new snapshot in the 1up view. ![Keyboard shortcuts](../../images/keyboard-shortcuts.png)
What about baselines on other branches? Chromatic automatically changes the baseline snapshots used for each build, depending on your branch. Each branch has a separate set of baselines. This means you can update UI components on multiple feature branches in parallel without conflicts. When you merge branches, the most recent baseline takes precedence. [Learn about branching and baselines »](/docs/branching-and-baselines)
How do I reproduce the snapshot? Sometimes, you need to look closer to determine why a snapshot is rendering as it does. Along with pixel and DOM diffs, Chromatic displays the interactive page just as it appears in your app and E2E tests. Click “Inspect snapshot” to open the Inspector. Switch between the “Canvas” and “Snapshot” tabs to compare the live component to the snapshot. Learn more about snapshots [here](/docs/snapshots). ![Reproduce snapshot](../../images/feature-component-inspect.png)
Can I retake a snapshot? Yes, [rerun the latest build](/docs/rerun-builds) on your branch to retake snapshots of unreviewed or denied changes. ![Rerun button](../../images/build-detail-rerun-button.png)
How are changes on builds different from those listed in the UI Review ‘Changeset’ tab? UI tests (shown on the build screen) detect changes between builds, specifically between the last accepted baseline and the latest build. This is useful for detecting defects during development and when merging to the main branch to ship. In contrast, [UI Review](/docs/review) shows the changeset between the latest commit on the PR branch (head) and the ‘merge base’ (base). Think of it as a code review but for UI.
Why is review disabled on the build page? Reviewing is only enabled for the latest build on a branch to ensure that only the most up-to-date UI is accepted as a baseline.
Why is commenting disabled on old builds? Comments are turned off on old builds to ensure that discussions are always on topic and up to date with the latest UI. This prevents the situation where different reviewers comment on different versions of the code.
Can I review the same commit on separate branches? Yes, but it‘s not a best practice. Every branch has independent baselines for each test until the branch gets merged. If two builds reference the same commit hash but are on _different branches_ it will be possible to review those builds separately so long as they're the latest build on their respective branches. We don't recommend this because you'll have to review the same change multiple times. Instead, we recommend regularly reviewing builds to keep feature branches 🟢 passing.
--- ## Troubleshooting
Why do I see "Didn't find any commits in this Git repository in the last X builds"? If you run into this situation, it is likely because across the number of unique commits across all builds connected to your project, Chromatic could not find a single one that exists in the repository. This can happen for various reasons (i.e., rebasing, squash merging). However, something has likely gone wrong if all previous X builds' commits are missing. If you see this message and can't resolve the issue, please get in touch with us via our in-app chat or email us at support@chromatic.com.
Why do I see "Failed to find common ancestors with most recent builds within X commits"? This is an uncommon issue that may happen only in specific cases due to some unusual configuration in your repository. If you see this message, it means that although we found a recent build connected to your repository history (see the item [above](#chromatic-build-no-commits)), we couldn't find any common history between your checked-out build and any other build in the latest number of commits. We recommend contacting us via our in-app chat or email us at support@chromatic.com for further assistance.
Why do I see "Build X is based on a commit without ancestor builds"? Unless you're creating the first build to establish baselines, when you generate a new build, Chromatic searches your repository history for the most recent build based on an ancestor commit (i.e., commits that are part of the history of the current commit). If it can't find any, it will show this message. However, this is an uncommon issue that may happen due to the following reasons: 1. You switched between branches and re-ran Chromatic without checking out the changes that installed Chromatic in the first place. In this situation, you can safely ignore the message and continue your workflow. 2. Your repository's Git history was rewritten via a rebase or squash merge (e.g., via GitHub's "Squash and Merge" or "Rebase and Merge" options), which you can quickly solve by referring to our CI documentation based on your CI provider. 3. You're working with a shallow clone of your repository to generate builds. Chromatic needs access to the entire Git history to establish baselines (or at least the history until the previous build), which you can address via our [baselines documentation](/docs/branching-and-baselines). 4. Some other unusual configuration in your repository is causing the issue, or there is an issue (e.g., an outage) on our end. If you are in this situation and having trouble resolving the issue, contact us via our in-app chat or email us at support@chromatic.com.
Chromatic doesn't work with my custom Storybook script We do our best to interpret your Storybook script in package.json, but you might need to pass additional options to the `chromatic` command. [Check out all the options »](/docs/cli)
Why do I get errored builds randomly? Chromatic builds and runs Storybook flawlessly _most of the time_, but we're not perfect (we wish). Sometimes builds don't run due to rare infrastructure issues. If this happens, try to re-run the build via your CI provider. We keep track of these errors to improve the service.
Why is my build failing with the message Cannot run a build with no stories? This happens if certain stories were disabled via the [`chromatic: { disableSnapshot: true }`](/docs/disable-snapshots#with-storybook) option at a higher level. To solve this you can: 1. Remove the top-level [`chromatic: { disableSnapshot: true }`](/docs/disable-snapshots#with-storybook) option 1. Enable snapshots for specific stories 1. Run `yarn storybook-build` locally and fix the issues in your stories
How can I troubleshoot issues in Storybook? From **Storybook 7.6** onwards, you can use the storybook doctor command to run checks and identify issues in Storybook: npx storybook@latest doctor This tool helps detect problems like mismatched Storybook versions, duplicated dependencies, and incompatible add-ons. For each detected issue, it offers detailed descriptions, references, and suggestions for follow-up actions. Learn more [here](https://storybook.js.org/docs/api/cli-options#doctor).
How can I debug syntax issues in my Storybook project? You can add the Storybook [ESlint plugin](https://www.npmjs.com/package/eslint-plugin-storybook) to help you debug Storybook syntax issues and comply with best practices.
Why is my build failing with the message ✖ Failed to build Storybook? To debug an issue with your Storybook project, run the commands below locally and confirm all stories are loading correctly without console log errors and warnings. ```shell ​npm run build-storybook npx http-server storybook-static -o ``` Use the same method to debug the following errors: `✖ Failed to verify your Storybook` `✖ Failed to publish your built Storybook` `✖ Failed to publish build - Failed to extract stories from your Storybook`
Why are styles not applied correctly in stories? If styles are not rendering correctly, there could be several reasons behind that: 1. **The order in which stylesheets are loaded.** - In development mode, styles from the preview might be imported earlier than component styles, while in production mode, this order could be reversed. This affects the specificity and, ultimately, the rendering of styles. If this is the case, the styles render but are overwritten by others. - In Storybook 6 and earlier, stylesheets were loaded upfront, while from Storybook 7 onwards, stylesheets are loaded on-demand when the story is viewed. This affects how styles load. 2. **Browser compatibility.** If the browser version does not support certain CSS features, styles may not apply correctly. Find browser versions supported by our Capture Infrastructure [here](/docs/infrastructure-release-notes). If you have additional questions, use our **in-app chat** to contact or email us at [support@chromatic.com](mailto:support@chromatic.com).
--- ## Diff Inspector URL: https://chromatic.com/docs/diff-inspector > Learn how to analyze visual changes with Chromatic's Diff Inspector # Analyzing visual changes with Chromatic's Diff Inspector Diff Inspector is a tool in the Chromatic web app that helps you see what visually changed between the test baseline and the new snapshot. Changed areas are automatically highlighted in neon green for immediate visibility. ![](../../images/diff-inspector.png) ## Diff view options Chromatic offers flexible ways to analyze visual changes: - **Unified view (1 Up):** See changes directly overlaid on top of your baseline for a focused comparison. - **Split view (2 Up):** Compare your baseline and new snapshot side-by-side, with changes clearly highlighted in the new version. ## Emphasize subtle changes Sometimes it can be tough to see subtle UI differences. For example, you might miss tweaks to properties like border-radius and font rendering. Chromatic offers two additional modes to help you pinpoint small UI changes faster. ### Diff strobing Diff strobing, a.k.a, _party mode_, emphasizes the highlight even more by quickly toggling between the baseline and the new snapshot. ![](https://chromaticblog.ghost.io/content/images/downloaded_images/Diff-Detector-2-0/1-CrOUFtW8-sndKXSKSS0eug.gif) ### Autofocus Focus mode is designed to save you valuable time by spotlighting minor changes. For maximum convenience, focus mode activates automatically when it senses minimal pixel variations. ![](https://chromaticblog.ghost.io/content/images/2020/10/autofocus-toggle.gif) ### Zoom-in When in focus mode, hover over the new snapshot to activate a magnifying glass. This zooms into the snapshot diff to help you see the change in greater detail. ![](https://chromaticblog.ghost.io/content/images/2020/10/autofocus-magnify.gif) ## Configure diffing behavior ### Adjust threshold for changes Customize how Chromatic identifies visual changes by adjusting the `diffThreshold` configuration option. The default setting strikes a balance between accuracy and minimizing false positives caused by minor rendering variations. Tailor this threshold to your specific component or library's needs. [Learn more about diff threshold »](/docs/threshold) {/* prettier-ignore-start */} ```ts title="MyComponent.stories.ts|tsx" // Replace your-framework with the framework you are using (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from "@storybook/your-framework"; import { MyComponent } from "./MyComponent"; const meta = { component: MyComponent, title: "MyComponent", } satisfies Meta; export default meta; type Story = StoryObj; export const StoryName: Story = { args: { with: "props", }, parameters: { // Sets the diffThreshold for 0.2 for a specific story. chromatic: { diffThreshold: 0.2 }, }, }; ``` ```js title="tests/mytest.spec.js" test.describe("some block", () => { // 👇 your option overrides test.use({ diffThreshold: 0.2 }); test("some test", async ({ page }) => { await page.goto("https://some-url.com"); // ... your test code }); }); ``` ```js title="cypress/e2e/some-test.cy.js|ts" it("A test that does something", { env: { diffThreshold: 0.2 } }, () => { cy.visit("https://some-url.com"); // ... your test code }); ``` {/* prettier-ignore-end */} ### Ignored regions You can configure Chromatic to ignore changes in certain parts of a snapshot by attaching the `chromatic-ignore` as a class or a data attribute to DOM elements. These ignored regions can then be easily highlighted within the Diff Inspector. [Learn more about ignoring elements »](/docs/ignoring-elements) --- ## Visual tests URL: https://chromatic.com/docs/visual > Chromatic is a cloud-based toolchain that integrates with Storybook, Playwright, and Cypress to help teams test and review UI components. # Visual testing with Chromatic Visual Tests are a powerful tool for catching visual regressions and ensuring your app functions as expected. They work by capturing [snapshots](/docs/snapshots) of every test within a cloud browser environment. Then, whenever you push code, Chromatic compares your new snapshots to [baseline versions](/docs/branching-and-baselines#whats-a-baseline) to identify visual changes. If changes are detected, you will be prompted to verify that they are intentional or fix any errors. ## Why use visual testing? Functional UI tests excel at ensuring your app _works_, but they often fail to catch bugs in UI appearance. This is because functional tests don’t validate the pixels rendered by your application UI. Instead, they only verify the logic and behavior of components and pages. For instance, buggy CSS might hide your app's “checkout” button behind a notification banner. Your [functional test](https://www.chromatic.com/blog/functional-testing/) might indicate that the button is clickable—because, technically, it is, even though it’s now inaccessible to your users. Visual testing catches such bugs and safeguards your app’s user experience. ## How does visual testing work? You can think of Chromatic’s visual testing as "before-and-after" [snapshots](/docs/snapshots) of your app's interface. You begin by capturing a perfect "before" image—this becomes your [baseline](/docs/branching-and-baselines#whats-a-baseline). After any code changes, Chromatic compares a new "after" snapshot pixel-by-pixel against the baseline, revealing any visual differences. Chromatic's streamlined workflow involves four steps: 1. **Cloud Rendering:** Chromatic renders your UI components in a cloud-based browser. 2. **Snapshot Capture:** Chromatic takes a snapshot for each test, with all tests running simultaneously to save you time. 3. **Automated diffing:** Whenever you update your code, Chromatic generates new snapshots and compares them to the baselines. 4. **Review and Verification:** When Chromatic detects changes, you’re prompted to review them to ensure they're intentional. Any unexpected changes trigger notifications so you can fix them quickly. ## How does Chromatic fit into my stack? Chromatic leverages your existing setup—configuration, mocking, and tests—to enable visual testing of your application's UI. That means you can verify how UIs look and function simultaneously. ### Visual testing with Storybook [Storybook](https://storybook.js.org) is an open source workshop for developing components and pages in isolation. Chromatic is built and maintained by the team behind Storybook, ensuring a seamless integration. Storybook lets you define the different states and variations of components as [stories](https://storybook.js.org/docs/get-started/whats-a-story). This gives you an ideal setup for functional testing. Attach a [play function](https://storybook.js.org/docs/writing-stories/play-function) to mimic how users interact with your components and include assertions to verify the expected behavior. Chromatic uses these stories to power visual tests. ![](../../images/workflow-addon.png) Chromatic integrates with Storybook via an [addon](https://www.chromatic.com/storybook) to unlock on-demand visual testing right from within your familiar Storybook environment. With a single click, you can run visual checks on all your stories simultaneously. Chromatic runs the tests in the cloud, notifying you directly in Storybook if any component's appearance deviates from expectations. In addition to this local workflow, you can also run these tests in CI. [**Get started with Storybook & Chromatic »**](/docs/quickstart) ### Visual testing with Playwright [Playwright](https://playwright.dev/) is an open source end-to-end (E2E) test runner developed by Microsoft. It verifies key app flows like "sign up" and "add to cart" by driving the browser to simulate user interactions. Chromatic integrates with Playwright by extending its test and expect utilities. While your Playwright tests run, Chromatic captures an archive of the page (including DOM, styling, and assets) and uploads it to the cloud. There, Chromatic generates snapshots and performs pixel diffing to identify any unintended visual changes. Playwright offers native visual testing capabilities, but Chromatic's unique snapshot approach and powerful diffing tools provide a more comprehensive testing experience. [**Get started with Playwright & Chromatic »**](/docs/playwright) ![](../../images/workflow-e2e-visual-test.png) ### Visual testing with Cypress [Cypress](https://www.cypress.io/) is another popular E2E test runner like Playwright, but it lacks built-in visual testing features. While Cypress tests can verify your app's functionality, they won't detect visual regressions in the interface. This is where Chromatic becomes an essential tool for Cypress users. Chromatic integrates with Cypress via a plugin. While your Cypress tests execute, Chromatic captures complete archives of your test cases (DOM, styling, and assets) and uploads them to the cloud. There, Chromatic generates snapshots and performs pixel diffing, enabling you to catch any unintended visual changes in your application. [**Get started with Cypress & Chromatic »**](/docs/cypress) --- ## Visual Tests addon for Storybook URL: https://chromatic.com/docs/visual-tests-addon > Configure Storybook to test UIs with the Visual Tests Addon # Visual Tests addon for Storybook Pinpoint visual bugs in local development without leaving Storybook. ## Set up Visual Tests Addon for Storybook Visual Tests Addon ensures that the dev-to-test feedback loop is as fast as possible by allowing you to run visual tests on demand. It lets you uncover visual bugs in local development without leaving Storybook. ### 1. Install the addon Add visual tests to your project by installing `@chromatic-com/storybook`: {/* prettier-ignore-start */} ```shell $ npx storybook@latest add @chromatic-com/storybook ``` ```shell $ yarn dlx storybook@latest add @chromatic-com/storybook ``` ```shell $ pnpm dlx storybook@latest add @chromatic-com/storybook ``` {/* prettier-ignore-end */}
Storybook 7.6 and higher required. Read the [migration guide](https://storybook.js.org/docs/releases/migration-guide) for help migrating Storybook versions.
When you start Storybook, you'll see a new addon panel for Visual Tests where you can run tests and view results. ![Once the addon is installed, you should see the Visual Tests Addon panel in your Storybook](../../images/visual-tests-enable.png)
🤖 **Need help or more customization?**
If you're experiencing issues with the `storybook@latest add` command or want to explore advanced configuration for the Visual Tests addon, be sure to check the [Storybook documentation »](https://storybook.js.org/docs/writing-tests/visual-testing)
### 2. Authenticate Sign in to Chromatic to create a new project or link an existing project. You’ll see a list of available projects that you have access to. Select a project to finish the setup. The addon will automatically adjust the configuration file, add the necessary project identifiers, and retrieve any existing baselines if available. Select a project from your project list to finish setup. If you're setting up the addon for the first time, the configuration files and necessary project identifiers will be added for you automatically. ![Once authenticated, you should see a menu to select an existing project or create a new one](../../images/visual-tests-project-selection.png) ### 3. Run tests Click the ▶️ Play button in the Storybook sidebar to run visual tests. This will send your stories to the cloud to take snapshots and detect visual changes. ![Storybook running visual tests with the addon. The play button displays a progress indicator](../../images/visual-tests-tests-running.png) ### 4. Review changes If there are visual changes in your stories, they will be 🟡 highlighted in the sidebar. Click the story and go to the Visual Tests addon panel to see which pixels changed. If the changes are intentional, ✅ accept them as baselines locally. If the changes aren't intentional, fix the story and rerun the tests using the ▶️ Play button. ![Approve or reject the UI changes in Storybook](../../images/visual-tests-accept-all.png) ### 5. Chromatic automatically syncs baselines to the cloud When you accept changes in the addon, your baselines automatically sync to the cloud. Your local-only baselines will graduate to standard baselines. What that means: - These baselines will be available for anyone who checks out your branch. - [TurboSnap](/docs/turbosnap) will skip the stories that haven’t changed. - Tests with accepted baselines don't need to be rerun or reviewed again, speeding up your pull request process. ![Visual Test addon confirms when a baseline is updated](../../images/visual-tests-approved.png) --- ## Addon configuration options Chromatic is configured using the [`./chromatic.config.json`](/docs/cli#chromatic-config-file) file. By default, the recommended configuration for most projects is already applied. You can also customize the default behavior and provide additional options for full control. The shortlist of options that are addon-specific are below. View the full list of [options](/docs/configure#options). | Option | Description | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `projectId` | Automatically configured. Sets the value for the project identifier
`"projectId": "Project:64cbcde96f99841e8b007d75"` | | `buildScriptName` | Defines the custom Storybook build script
`"buildScriptName": "deploy-storybook"` | | `debug` | Output verbose debugging information to the console
`"debug": true` | | `zip` | Recommended for large projects. Configures the addon to deploy your Storybook to Chromatic as a zip file
`"zip": true` | ```json title="./chromatic.config.json" { "projectId": "Project:64cbcde96f99841e8b007d75", "buildScriptName": "deploy-storybook", "debug": true, "zip": true } ```
Custom addon config files & environments If you have separate config for different environments, use `configFile` to specify which file to load. Here's how you'd apply one config for `development` and another for `production`. ```ts title=".storybook/main.ts" // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], addons: [ // Other Storybook addons { name: '@chromatic-com/storybook', options: { //👇 Loads the configuration file based on the current environment configFile: process.env.NODE_ENV === 'development' ? 'chromatic.config.json' : 'production.config.json', }, }, ], }; export default config; ```
--- ## Frequently asked questions
What are "local" builds and how are they different from builds? A build is an automated visual test run. When you set up Chromatic in CI, a build runs every time you push a commit to your repository. With the addon, you're able to visually test uncommitted code. This is useful when you want to quickly check your work in progress for visual changes. The addon creates local builds to designate ephemeral uncommitted code from normal builds which use committed code. "Local" just means local to you. Updating baselines in local builds only affects your other local builds on a given branch. This allows you to iterate quickly in development without affecting teammates. When you want to update the baselines with teammates, you commit and push your work, which triggers a normal build via CI. If the code is the same between your last local build and the normal build triggered by CI, Chromatic will automatically auto-accept baselines in the normal build so that you don't need to review twice. If you have TurboSnap, Chromatic will intelligently test only the stories that changed and copy over snapshots from stories that were unchanged.
What’s the difference between testing with the addon vs. CI? The addon allows running tests on-demand to detect bugs earlier in development. It saves time because you don’t have to wait for CI jobs to finish running. But the addon doesn't replace CI, Chromatic still requires CI to do its job.
Why are Doc pages missing in the published Storybook generated by my "local" build? By default, [Docs](https://storybook.js.org/docs/writing-docs) are disabled in local builds created by Visual Tests addon. You can enable Docs by setting the following flags: `disableBlocks`, `disableAutoDocs`, `disableMDXEntries`, and `disableDocgen` to `false` in your `.storybook/main.ts`. Learn more about these flags [here](https://storybook.js.org/docs/api/main-config/main-config-build#test). ```ts title=".storybook/main.ts" // Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite) import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { // ... build: { test: { disableBlocks: false, disableAutoDocs: false, disableMDXEntries: false, disableDocgen: false, }, }, }; export default config; ``` That said, we don't recommend turning Docs on for local builds because this will cause the build time to increase considerably.
Can I use the addon with a monorepo? Yes, but you must have a separate `chromatic.config.json` file for each subproject (e.g., `design-system-A/chromatic.config.json` and `design-system-B/chromatic.config.json`). You cannot use the same file to run builds for multiple Chromatic projects. Ensure that flags like like `storybookBaseDir`, `storybookBuildDir` and `storybookConfigDir` are set correctly for each subproject.
--- ## Troubleshooting
Running Storybook with the addon enabled throws an error When running Storybook with the addon enabled, you may encounter the following error: ```shell const stringWidth = require('string-width'); Error [ERR_REQUIRE_ESM]: require() of ES Module /my-project/node_modules/string-width/index.js is not supported. ``` This is a [known issue](https://github.com/storybookjs/storybook/issues/22431#issuecomment-1630086092) when using an older version of the Yarn package manager (e.g., version 1.x). To solve this issue, you can upgrade to the latest stable version. However, if you cannot upgrade, adjust your `package.json` file and provide a resolution field to enable the Yarn package manager to install the correct dependencies. In doing so, you may be required to delete your `node_modules` directory and `yarn.lock` file before installing the dependencies again. ```json title="package.json" { "resolutions": { "jackspeak": "2.1.1" } } ```
Why am I seeing the "Login Error: Failed to fetch initial project list" error? This error typically means Chromatic's Visual Tests Addon cannot retrieve your list of projects during the login or authentication step. Here are the most common causes and how to resolve them: #### 1. Missing or invalid `chromatic.config.json` file Ensure your project has a `chromatic.config.json file` in the root directory with a valid `projectId`. Example: ```json title="chromatic.config.json" { "$schema": "https://www.chromatic.com/config-file.schema.json", "projectId": "Project:your_project_id_here" } ``` - This file must be committed to your repository. - The `projectId` is different from your `projectToken`. It's the number from the Chromatic link. #### 2. Insufficient GitHub permissions If Chromatic cannot retrieve your projects, your git provider account may not have the necessary access. Check: - You granted access to the correctgit provider organization when authorizing Chromatic. - You're logged in with the same git provider account with access to the target project. - Try signing in via an incognito or private browser window to avoid caching or token issues. #### 3. Blocked network requests If your network blocks requests to Chromatic’s API, the Visual Tests Addon may not work properly. Check your network: - Confirm you can access `https://www.chromatic.com/api` and `https://index.chromatic.com/graphql` from your browser. - If you use a corporate proxy, VPN, firewall, or SSL inspection, they might be interfering with the request.
Does the addon affect snapshot usage? Snapshots taken by the Visual Tests addon count toward the snapshots included in your Chromatic plan. If you have any questions about snapshot costs or billing, please contact us via in-app chat.
Does the addon support TurboSnap? Yes. Visual Tests addon supports TurboSnap via the [`./chromatic.config.json`](/docs/cli#chromatic-config-file) file.
Can I deny a change with the addon? No. Denying changes is only available when running builds from CI or the CLI. If you need to, you can revert changes by clicking the ”Unaccept” button in the addon panel.
Do I need Git to run the addon? Yes, Visual Tests addon requires Git to track baselines for each story. To use Chromatic, you need to have git initialized in your project repository and have at least one commit.
--- ## Accessibility Tests URL: https://chromatic.com/docs/accessibility > Catch accessibility issues early with automated testing for your components # Accessibility testing with Chromatic Chromatic builds on Storybook and [axe](https://github.com/dequelabs/axe-core) to seamlessly integrate accessibility testing into your development workflow. You write stories to create test cases for your components and run accessibility checks locally within Storybook. Then when you're ready to merge, Chromatic automatically runs accessibility regression tests on all your stories through CI. It also provides a dashboard that offers a bird's-eye view of [WCAG](#what-are-web-content-accessibility-guidelines-wcag) issues across all your components. ## Why component-level accessibility tests? Traditional page-level tests fail to address accessibility at its source. For decades, the best practice was to navigate page by page and run accessibility scans along the way to compile an audit report. This method is too noisy. When issues are identified at the page level, the root cause often lies deep within a component. You have to sift through thousands of violations to determine which component caused the problem. One component impacts countless pages What’s more, page-level tests occur too late in the development cycle. Developers first build components and then assemble them into pages. One flawed component can spread issues throughout your app, making problems harder to identify and more time-consuming to fix. Chromatic runs accessibility tests at the component-level, where issues are easier to identify and fix. By integrating accessibility testing into your development workflow, Chromatic helps you catch violations early, before they multiply across pages. ## Why use regression-based accessibility testing? Axe has long been the industry standard for running automated accessibility tests on HTML-based interfaces. These tests audit the rendered DOM against a set of heuristics based on [WCAG](https://www.w3.org/WAI/standards-guidelines/wcag/) rules and widely accepted best practices. They act as the first line of QA to catch blatant accessibility violations. When you run axe, it gives you a list of _all_ violations detected across the UI being tested. While this is incredibly helpful, it's unlikely you'll address all issues immediately. Teams often inherit accessibility debt that can't be resolved overnight. They want to address these issues incrementally while releasing new features without adding more debt. By tracking violations against a story's baseline, Chromatic helps prevent regressions in pull requests while tracking debt so you can fix it separately at your own pace. ## Accessibility testing workflow By combining Chromatic and Storybook, you get instant accessibility feedback at every phase, from development to running checks on CI. Here’s how it works: ### During development: Fast feedback in Storybook Storybook’s [Accessibility addon](https://storybook.js.org/docs/writing-tests/accessibility-testing) simplifies running axe on individual components. You can run accessibility tests for all your stories in the background. If there are any violations, the test will fail, and you will see a summary in the sidebar. Violating elements are highlighted in Storybook’s canvas, allowing you to pinpoint the exact problem areas. You also get detailed error descriptions and guidance to help resolve issues quickly. ![Storybook UI with accessibility features annotated](../../images/a11y/addon-a11y-annotated.png) ### In CI: Regression-checks with Chromatic When you run Chromatic, it creates a static build of your Storybook and uploads it to our Capture Cloud. From there, Chromatic spins up a fleet of standardized browsers to load all your stories in parallel and run accessibility tests. Chromatic tracks accessibility violations over time, establishing a baseline for each story. This helps separate pre-existing issues from new ones.
What is an accessibility baseline? In [Visual testing](/docs/visual), a baseline is the last known “good” state of the story. Subsequent snapshots are compared to this baseline to identify visual changes. ![Chromatic build showing the baseline and newly captured snapshots with visual changes highlighted in green](../../images/a11y/snapshot.png) In Accessibility testing, a baseline is a set of [accessibility violations](#what-is-an-accessibility-violation) detected for a story. Subsequent snapshots are compared to this baseline to identify new violations or to confirm resolved issues. Here's an example: Build #1 identifies several violations for this story. By accepting the baseline, 17 violations (across 3 rules) are added to the story's baseline. ![Build one with three types of violations: insufficient color contrast, links need discernable text and invalid ARIA values ](../../images/a11y/build-1.png) After making code changes, you run Build #2. Chromatic finds 10 additional violations across 2 rules. This difference is shown on the test page. You can also view violations from previous builds by expanding the table. If you accept this snapshot, these 10 new violations will be added to the baseline. ![Build 2 with two types of violations: missing form labels and images lack alt text. The three previously accepted rules are hidden by default.](../../images/a11y/build-2.png)
In subsequent builds, Chromatic takes a new snapshot of accessibility violations for each story and compares them against the baselines. If there are new or altered violations, Chromatic flags them for your review. ![Chromatic webapp displaying a test page with accessibility results](../../images/a11y/accessibility-tests-test.png) ## Why run accessibility tests in both Storybook and Chromatic? ### Complementary testing approaches **Storybook** provides developers with a fast feedback loop. You can test components—that you're working on—during development and receive instant feedback on accessibility issues. This enables you to fix problems locally before committing code and to identify and address violations in real time as you work. **Chromatic** provides ultimate coverage for teams by testing across all viewports, modes, and user preferences. It runs tests consistently in CI environments, tracks violations over time, and ensures no regressions slip through the cracks. This comprehensive approach safeguards your entire project's accessibility standards. ### Single run vs Regression tracking **Single run (Storybook)** shows all violations at a specific point in time, offering a comprehensive view of your component's current state. However, reviewing these results can be overwhelming, especially in large codebases with accessibility debt. **Regression tracking (Chromatic)** [tracks violations from commit to commit](/docs/accessibility#how-does-chromatic-track-each-unique-violation) and only flags new or changed violations. This makes it easier to fix issues incrementally and visualizes progress over time. ![The accessibility tab highlights new violations detected in a particular build. Previously accepted violations are hidden but can be accessed by clicking the button at the bottom of this section.](../../images/a11y/accessibility-tab.png) For a complete example of regression tracking, see the [What is an accessibility baseline](/docs/accessibility#what-is-an-accessibility-baseline) section above. ### Accountability and team workflow The regression approach allows teams to distinguish between legacy accessibility debt and new issues, making the work more manageable. Progress becomes visible and measurable, which helps maintain momentum in your accessibility improvement efforts. With regression tracking, you can easily identify which violations were introduced in a specific pull request. Much like adding tests to meet coverage thresholds, this ensures developers address accessibility issues in their pull requests to avoid adding to the accessibility debt. --- ## Frequently asked questions
What are Web Content Accessibility Guidelines (WCAG)? The [Web Content Accessibility Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/) are a set of internationally recognized standards developed by the World Wide Web Consortium (W3C) through their Web Accessibility Initiative (WAI). These guidelines provide a comprehensive framework for making web content more accessible to people with disabilities.
What is an accessibility violation? Accessibility violation refers to an issue detected within a story where an element fails to meet accessibility standards or guidelines (such as WCAG). These violations indicate barriers that might prevent people with disabilities from using the website or application effectively.
Why might you want to accept accessibility violations? TL;DR: You’ll want to accept violations if they're part of necessary structural changes leading to an eventual fix. When you run axe, it lists all accessibility violations found in the story. However in some cases, addressing all issues immediately may not be feasible. In that case, you’d accept the violations and fix them incrementally. Baselines help track accessibility issues from commit to commit as you make code changes, showing what was fixed and identifying any new issues introduced.
How does Chromatic track each unique violation? Chromatic identifies unique violations using three characteristics: 1. Type of violation (see [full classification](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md)) 2. Specific DOM node (e.g., ``, `
What happens when I fix an accessibility issue? Fixes are auto-accepted to the baseline, [learn more »](/docs/accessibility/usage#fixes-are-automatically-accepted)
Can I run Accessibility Tests with Playwright or Cypress? No, Chromatic only offers Accessibility Testing for Storybook.
Can I choose to only run accessibility tests? Not yet. `parameters.chromatic.disableSnapshot` will disable both visual and accessibility tests. We're working on allowing you to disable them accessibility and visual tests individually.
How do I configure Chromatic in CI for Accessibility Tests? If you have already integrated Chromatic in your CI setup, no changes are needed for accessibility tests to work. If you haven’t integrated Chromatic in your CI setup, please follow [this guide](/docs/ci).
Does Chromatic report "Needs Review" or "Incomplete" checks? No, Chromatic does not report these checks. There are instances where axe-core is unable to accurately determine a violation. For example, it can't accurately assess color contrast when text is rendered over a gradient or background image. You will see these issues reported in Storybook’s Accessibility panel under the "Incomplete" tab. However, Chromatic only reports on violations it can be certain about.
Does TurboSnap support accessibility tests? Yes. TurboSnap analyzes your project’s Git history and [dependency graph](https://webpack.js.org/concepts/dependency-graph/) to identify which components and their dependencies have changed. It then only snapshots and runs visual and accessibility tests for stories associated with those changes. For the rest, it copies over the snapshots from baselines that didn’t change.
Do accessibility tests support modes? Yes, if you've configured modes for a story, Chromatic will run accessibility tests on each mode too.
Do accessibility tests support different browsers? No, accessibility tests are only executed in Chrome.
Do accessibility tests support viewports? Yes. Similar to how it works for visual tests, if you've set up viewports for a story, accessibility tests will run for each viewport.
Do accessibility tests support custom rules? Not yet. This means that if you have custom [rules and checks](https://github.com/dequelabs/axe-core/blob/64d409dc5862e9fdebcec87a0a269ab3f3e71ad2/doc/rule-development.md) defined in your Storybook configuration, they will not run with accessibility tests.
Does Chromatic check for prefers-reduced-motion violations? Not yet
## Troubleshooting
Why are ignored regions not working with Accessibility Tests? Ignored regions are a feature specific to visual tests and aren't supported for accessibility and interaction tests.
I expected certain accessibility violation to be flagged—such as missing form input labels, tables without proper headers—but I'm not seeing them? Ensure that the rule is enabled in your Storybook [A11y addon configuration](https://storybook.js.org/docs/writing-tests/accessibility-testing#configure). If it is enabled, verify whether the check is running in Storybook. While you might expect a violation, the check could actually be passing. For example, consider: ```html ``` You might expect a "missing form input labels" violation, but the [placeholder attribute can provide text inputs with an accessible name](). Similarly, table header rules only apply if the table has more than three rows or columns.
Why am I seeing different accessibility test results in Storybook vs Chromatic? Storybook shows all violations at a specific point in time. Where are as, Chromatic tracks violations from commit to commit and only flags [new or changed violations](/docs/accessibility#why-use-regression-based-accessibility-testing). For more on how to combine the two into an effective workflow, refer to [this section](/docs/accessibility#why-run-accessibility-tests-in-both-storybook-and-chromatic).
Why is Chromatic not reporting accessibility in the iFrame nested within my story? Currently Chromatic does not support checking accessibility violations within iframes (nested within a story). That's because axe is only loaded at the story level and not within the nested iFrames.
--- ## Configure URL: https://chromatic.com/docs/accessibility/configure > Learn about the various configuration options for accessibility tests # Configure Accessibility tests You can configure Chromatic's accessibility tests to match your project's specific requirements. By default, Chromatic runs accessibility tests with the following rules: - [WCAG 2.0 Level A & AA Rules](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md#wcag-20-level-a--aa-rules) - [WCAG 2.1 Level A & AA Rules](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md#wcag-21-level-a--aa-rules) - [Best Practices Rules](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md#best-practices-rules) The [region rule](https://dequeuniversity.com/rules/axe/4.1/region) _is_ disabled because you're testing individual stories, not entire pages, so the story will likely lack the HTML5 landmark element. ## How do I configure the Accessibility addon? All configuration—such as selecting which rules to evaluate the UI against or enabling/disabling specific axe features—is managed through Storybook. The Accessibility addon is configured via the `a11y` parameter which accepts the following properties: - **`element` (optional):** the selector to inspect. Defaults to `body`. - **`config`:** Options passed to `axe.configure`. See [axe.configure API docs](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure) to learn more about the available options. - **`options`:** axe's options parameter. See [options parameter API docs](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter) to learn more about the available options. Here's an example configuration for the Accessibility addon in Storybook: ```tsx title=".storybook/preview.ts" // Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite) // if you're using Storybook 9, or with the appropriate renderer otherwise. import type { Preview } from '@storybook/your-framework'; const preview: Preview = { parameters: { a11y: { // Optional selector to inspect element: 'body', /* * Options passed to axe.configure * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axeconfigure * to learn more about the available options. */ config: { rules: [ { // The autocomplete rule will not run based on the CSS selector provided id: 'autocomplete-valid', selector: '*:not([autocomplete="nope"])', }, { // Setting the enabled option to false will disable checks for this particular rule on all stories. id: 'image-alt', enabled: false, }, ], }, /* * axe's options parameter * See https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter * to learn more about the available options. */ options: {}, }, }, initialGlobals: { a11y: { // Optional flag to prevent the automatic check within Storybook manual: true, }, }, }; export default preview; ```
For more on configuring the Accessibility addon, refer to the [Storybook docs](https://storybook.js.org/docs/writing-tests/accessibility-testing#configure). ## Run all rules for a specific compliance standard You can use tags to run rules specific to a standard, such as `WCAG 2.2 Level AA`, `EN-301-549` (European Accessibility Act), etc. For example: ```tsx title=".storybook/preview.ts" // Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite) // if you're using Storybook 9, or with the appropriate renderer otherwise. import type { Preview } from '@storybook/your-framework'; const preview: Preview = { parameters: { a11y: { configure: {}, options: { runOnly: { type: 'tag', // eg: If you wanted to specifically meet the standards of European Accessibility Act // Full list of available tags: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#axe-core-tags values: ['EN-301-549'], }, }, }, }, }; export default preview; ``` ## Disable accessibility tests ### Both in Storybook and Chromatic For Storybook 8+, use the [a11y global](https://storybook.js.org/docs/writing-tests/accessibility-testing#disable-automated-checks) to disable accessibility tests for a story and/or component, both in Storybook and Chromatic. ```tsx title="MyComponent.stories.ts|tsx" // Adjust this import to match your framework (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from '@storybook/your-framework'; import { MyComponent } from './MyComponent'; const meta = { component: MyComponent, } satisfies Meta; export default meta; type Story = StoryObj; export const NonA11yStory: Story = { globals: { a11y: { // This option disables all automatic a11y checks on this story manual: true, }, }, } satisfies Story; ```
For older versions of Storybook, use the `a11y` parameter to disable accessibility tests. Refer to the [parameters documentation](/docs/config-with-story-params) to learn more about defining parameters.
### Only in Chromatic To disable Accessibility Tests only within Chromatic you can use the [`chromatic.disableSnapshot` parameter](/docs/disable-snapshots#with-storybook). Note, this will also disable visual tests for this story and/or component. ```tsx title="MyComponent.stories.ts|tsx" // Adjust this import to match your framework (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from '@storybook/your-framework'; import { MyComponent } from './MyComponent'; const meta = { component: MyComponent, title: 'MyComponent', parameters: { // Disables Chromatic tests on a component level chromatic: { disableSnapshot: true }, }, } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = {}; ``` --- ## Dashboard URL: https://chromatic.com/docs/accessibility/dashboard > Monitor accessibility compliance across your application with Chromatic's dashboard # Accessibility dashboard The Accessibility dashboard offers a comprehensive overview of accessibility issues in your components. Chromatic continuously monitors accessibility violations, enabling you to track compliance trends and prioritize remediation efforts effectively. ![Chromatic Accessibility dashboard showing 37,402 violations across 127 components and 263 total tests. The main chart displays violation trends from January to June. Below it is a table listing components by violation count.](../../images/a11y/accessibility-dashboard.png) ## Real-time tracking and historical insights Chromatic samples accessibility violations daily and reports on key metrics including total violations, components tested, and total test count. The trend visualization shows how your project's accessibility compliance evolves over time, helping you measure the impact of remediation efforts and identify patterns in violation occurrences. ## Component-level analysis The data table ranks components by violation count in descending order, immediately highlighting areas requiring urgent attention. Each component displays its violation count with severity classification and last update timestamp. This systematic approach enables teams to address accessibility debt by focusing resources on components with the highest user impact first. ## Export reports Download a CSV file containing all accessibility violations, associated components, and story details. This report streamlines compliance documentation and supports regulatory requirements such as Voluntary Product Accessibility Template (VPAT) submissions. ## Plan-based limits Chromatic provides seven days of data history with access limited to the default branch. Default branch is set as the `main` branch of your project. If there is no `main` branch, Chromatic will fall back to one of `master`, `develop`, `dev` or `rc`. Enterprise plans can upgrade to the _accessibility package_ for volume pricing, and full historical data, and multi-branch tracking in the dashboard. Contact our [sales team](https://www.chromatic.com/sales) to learn more. ### Frequently asked questions
Why do the impact values in the report look different from what I see in the dashboard? The CSV report uses a structured format for impact values (e.g., `3-critical`) based on axe's [accessibility guidelines](https://docs.deque.com/devtools-mobile/2023.4.19/en/impact) to facilitate data processing, sorting, and prioritization of violations. This format ensures compatibility with accessibility compliance tools and enables automated processing for regulatory submissions.
--- ## Usage URL: https://chromatic.com/docs/accessibility/usage > Learn how to setup and run accessibility issues using Storybook and Chromatic # Set up and run accessibility tests This guide will walk you through setting up and using Chromatic's accessibility testing in your workflow. To get started, you need to have Chromatic set up for your Storybook project. If you haven't done this yet, follow the [quickstart guide](/docs/quickstart) to get started. And ensure that you are using Storybook version 6.5+ and have [Accessibility addon](https://storybook.js.org/docs/writing-tests/accessibility-testing) installed and enabled. ## 1. Enable Accessibility Tests Enable Accessibility Tests from your project’s manage page. ![From the Manage page, select the "Automate" tab and look under the "Tests" section for "Accessibility tests"](../../images/a11y/enable-accessibility-tests.png) ## 2. Run a build to establish baselines Once enabled, run a build on your branch of choice to establish accessibility [baselines](/docs/accessibility#what-is-an-accessibility-baseline). Use either the CLI or your existing [CI setup](/docs/ci) to run the build. {/* prettier-ignore-start */} ```shell $ npx chromatic --project-token ``` ```shell $ yarn dlx chromatic --project-token ``` ```shell $ pnpm dlx chromatic --project-token ``` {/* prettier-ignore-end */} During the initial run, any accessibility violations detected are automatically added to the baseline. This is the starting point, you'll address issues and catch new regressions in future builds. ![On the build page you should see a banner confirming that accessibility testing has been enabled and that new baselines were created and auto-accepted](../../images/a11y/accessibility-tests-turned-on.png) ## 3. View changes between builds On subsequent builds, Chromatic compares new snapshots to existing baselines from previous builds. Try modifying the code to introduce new accessibility violations or fix an existing one and running another build. The list of changes is shown on the build page in the Chromatic web app. The build will be marked “unreviewed” and the changes will be listed in the “Tests” table. ![On the build page, you'll get a list of tests with changes. In the results column, you can see what kind of change was detected: visual or accessibility.](../../images/a11y/accessibility-tests-build.png) ## 4. Debug accessibility issues With each build, Chromatic identifies any changes in accessibility violations related to a story. However, it's up to you to verify if those changes are intentional or if they represent regressions that need to be fixed. Click on a test to view the details. ![When reviewing a specific test, Chromatic enables you to inspect violations and offers suggested fixes. You can also launch the published Storybook to debug live in your browser with devtools.](../../images/a11y/accessibility-tests-test.png) Problematic DOM nodes are highlighted directly in the component snapshot. Click them to open a tooltip with additional context. ![Offending nodes are highlighted in red within the snapshot](../../images/a11y/accessibility-tests-popover.png) You can also scroll down to the accessibility tab for more information on each specific violation. From there, you can **inspect violations with detailed descriptions** and suggested fixes. ![Expand the violation accordion to view details and suggested fixes](../../images/a11y/violation-expanded.png) ### Collaborate with teammates Click on **Go to Storybook** to navigate to the published Storybook in your own browser to debug with devtools. Share **permalinks** to specific violations to collaborate on a fix. ## 5. Verify changes Go through each snapshot to review the diff and approve or reject the change. **Unexpected violations** must be fixed before merging. These are regressions you didn't intend to introduce. **Expected violations** might seem counterintuitive, but they are common during incremental improvements. For example, you might restructure a component in a way that temporarily introduces new violations as part of a larger accessibility or feature overhaul. In these cases, you can accept these violations into your baseline because they represent an intermediate step toward a complete fix. By accepting expected violations into the baseline, you establish a new reference point for future comparisons. ### Fixes are automatically accepted Only new or changed violations need to be reviewed. If a build has no new accessibility violations but you resolve several existing issues, that build will pass, baselines will update, and no review will be required. ### Accessibility-only changes Some commits may add accessibility violations that have no associated visual changes. For example, _"missing a label on an input field"_ or _"missing an alt tag for an image"_. In such cases, you'll just see the latest snapshot with the problematic DOM nodes highlighted. ![Example of an Accessibility-only change. Chromatic only displays the latest snapshot and doesn't show the baseline snapshot.](../../images/a11y/accessibility-a11y-only.png) ### Non-visual violations Some accessibility violations do not have a visible DOM node associated with them. For example, _"Ensure every HTML document has a lang attribute"_ or _"Ensure `` does not disable text scaling and zooming"_. In such cases, there will be no visual preview of the violation. ![](../../images/a11y/accessibility-non-visual.png) ## 6. PR check for interaction tests ![PR badge for UI Tests](../../images/prbadge-test.png) If you have already integrated Chromatic in your CI setup, no changes are needed for accessibility tests to work. Otherwise, [integrate Chromatic into your CI pipeline](/docs/ci) to automatically run accessibility tests on every pull request. If new violations are detected, the PR will be marked as "unreviewed" until you review, accept, or fix them. --- ## Interaction tests URL: https://chromatic.com/docs/interactions > Learn how interaction testing works with Chromatic # Interaction testing with Chromatic Interaction tests enable you to verify how a component responds to user behaviors like click, type, keyboard, and hover. It's powered by Storybook's [`play`](https://storybook.js.org/docs/writing-stories/play-function) function.
Interaction tests require Storybook 6.5.10+. Check which version you have in package.json or open the "About Storybook" page in your Storybook.
## How to write interaction tests Add a [`play`](https://storybook.js.org/docs/writing-stories/play-function) function to your component's story to enable interaction tests. For example, if you want to validate a component's behavior write the following story: ```ts title="RangeSlider.stories.ts|tsx" // Adjust this import to match your framework (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from '@storybook/your-framework'; /* * Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0. * Refer to the Storybook documentation for the correct package and imports for earlier versions. */ import { expect } from 'storybook/test'; import { RangeSlider } from './RangeSlider'; const meta = { component: RangeSlider, title: 'Library/Charts/RangeSlider', } satisfies Meta; export default meta; type Story = StoryObj; export const InputRange: Story = { play: async ({ canvas, userEvent }) => { // 🔢 Type into input field await userEvent.type(canvas.getByTestId('input-max-range'), '15'); // ✅ Assert that component is responding to user behavior const availableOptions = await canvas.findAllByTestId('highlighted-bar'); await expect(availableOptions.length).toBe(15); }, }; ```
Read Storybook's interaction testing [docs](https://storybook.js.org/docs/writing-tests/interaction-testing).
![Storybook passed tests](../../images/interaction-test-storybook-passed-test.png) Chromatic waits for interactions to pass before capturing a snapshot. ### Confirm interaction tests are working Interaction tests run behind the scenes without you having to configure anything. To verify that they are working in Chromatic, publish your Storybook either via [CLI](/docs/cli) or [CI](/docs/ci). You can confirm that they’re running with the “Interaction” label in the Build page’s Tests section. ![Confirm interaction test run in the build summary](../../images/interaction-test-buildsummary-confirm.png) ## Composing stories with the `play()` function Similarly to `args`, `play()` functions can be [composed](https://storybook.js.org/docs/writing-stories/play-function#composing-stories) to reuse and reduce the amount of code being written. This can be helpful for instances where you are looking to snapshot multiple states of a longer interaction, or if you have interactions that can be reused to further compose other states of your component. An important caveat to remember when invoking a `play()` function from another story is that you must pass the _full context_ as an argument to the `play()` function. The example below shows how to do this correctly. ```ts title="MyComponent.stories.ts|tsx" // Adjust this import to match your framework (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from '@storybook/your-framework'; /* * Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0. * Refer to the Storybook documentation for the correct package and imports for earlier versions. */ import { expect, screen, waitFor } from 'storybook/test'; import { MyComponent } from './MyComponent'; const meta = { component: MyComponent, title: 'My Component', } satisfies Meta; export default meta; type Story = StoryObj; export const FirstStory: Story = { play: async ({ canvas, userEvent }) => { const dropdownButton = canvas.getByRole('button'); await userEvent.click(dropdownButton); }, }; export const SecondStory: Story = { play: async ({ canvas, context, userEvent }) => { // 👇 Pass the full context as an argument to the play function: await FirstStory.play(context); const findText = canvas.getByText('some text'); await userEvent.hover(findText); await waitFor(() => expect(findText.focus())); }, }; export const ThirdStory: Story = { play: async ({ context, userEvent }) => { // 👇 SecondStory.play will execute the play functions from FirstStory.play since this is part of the SecondStory.play function: await SecondStory.play(context); const searchbox = screen.getByRole('searchbox', { label: 'Search' }); await userEvent.type(searchbox, 'text for searchbox'); }, }; ``` Additionally, you can stack multiple `play()` functions within a story. Below is a quick pseudocode example. ```ts title="MyComponent.stories.ts|tsx" // Adjust this import to match your framework (e.g., nextjs, vue3-vite) import type { Meta, StoryObj } from '@storybook/your-framework'; /* * Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0. * Refer to the Storybook documentation for the correct package and imports for earlier versions. */ import { screen } from 'storybook/test'; import { MyComponent } from './MyComponent'; const meta = { component: MyComponent, title: 'My Component', } satisfies Meta; export default meta; type Story = StoryObj; export const FirstStory: Story = { play: async ({ canvas, userEvent }) => { await userEvent.type(canvas.getByTestId('an-element'), 'some text'); }, }; export const SecondStory: Story = { play: async ({ canvas, userEvent }) => { await userEvent.type(canvas.getByTestId('another-element'), 'some more text'); }, }; export const CombinedStories: Story = { play: async ({ canvas, context }) => { // 👇 This executes FirstStory.play and SecondStory.play before executing the story's play function: await FirstStory.play(context); await SecondStory.play(context); await userEvent.type(canvas.getByTestId('another-one'), 'even more text'); }, }; ``` ## Debug test failures Chromatic notifies you when an interaction errors or an assertion fails. We designate these as critical failures that need immediate attention. You won’t be able to pass the build until the test is fixed. ![Build page with failed interaction test](../../images/interaction-build-screen-failed-test.png) To find out which steps failed in your interaction test, click on the change to see a snapshot of the state where the error occurred. You'll see a detailed log and browser environment metadata to help with reproductions. ![Test page with failed interaction test](../../images/interaction-test-screen-failed-test.png) ### Reproduce test failures with a URL Go to your published Storybook to reproduce the exact state of your story when the test failed. Click the "View Storybook" button on the test page to open the failed story with the error message visible. Share the link with teammates to get a second opinion. ![Storybook with failed interaction test](../../images/interaction-test-storybook-failed-test.png) ## PR check for interaction tests Interaction tests are reported in the UI Tests pull request check. When a test fails, you'll see a "Failed tests" status message prompting you to fix the test before moving on. ![Failed interaction tests in CI](../../images/interaction-pr-check-failed-test.png) --- ## Frequently asked questions
My interactions aren't getting snapshotted consistently with external web fonts? Interactions run as soon as the DOM loads. But external resources like web fonts can load before or after the interaction runs depending on network latency. This can cause dialogs, tooltips, and menus to change position. We recommend [preloading fonts](/docs/font-loading) to ensure they're available when the DOM renders. If preloading is not possible, try adding a [delay before running interactions](/docs/delay#use-assertions-to-delay-snapshot-capture).
What happens when an interaction test fails? When interaction tests fail, the story will be badged with “Failed test.” You will not be able to “pass” a build that has failed tests. Fix interaction tests in Storybook and run the build again.
Does Chromatic capture snapshots of every step of an interaction test? No, Chromatic waits for the entire play function to execute and captures a snapshot only at the end. If you need a snapshot of a specific step, we recommend breaking your story into multiple stories and using [play function composition](#composing-stories-with-the-play-function).
--- ## Testing Shadow DOM URL: https://chromatic.com/docs/interactions/shadow-dom > Learn how write interaction tests for components that use Shadow DOM # Testing Shadow DOM with Storybook and Chromatic With the help of [shadow-dom-testing-library](https://github.com/konnorrogers/shadow-dom-testing-library), you can write tests that query elements inside the shadow root just like you would with standard DOM elements. This approach is fully compatible with Chromatic, which will accurately capture the UI state by waiting for interactions to complete. ## Configure Shadow DOM Queries in Storybook Preview Install `shadow-dom-testing-library` and then in your preview file, inject shadow-aware query methods into the `canvas` object using `beforeEach()`. ```ts title=".storybook/preview.ts" // Replace the @storybook/web-components-vite package with @storybook/web-components if you're not using Storybook 9.0 import type { Preview } from '@storybook/web-components-vite'; import { within as withinShadow } from 'shadow-dom-testing-library'; const preview: Preview = { beforeEach({ canvasElement, canvas }) { Object.assign(canvas, { ...withinShadow(canvasElement) }); }, }; // Extend TypeScript types for safety export type ShadowQueries = ReturnType; declare module 'storybook/internal/csf' { // Since 8.6 interface Canvas extends ShadowQueries {} } export default preview; ``` This adds methods like `findByShadowRole`, `findAllByShadowRole`, etc., directly to the `canvas` object in `play()` functions. ## Querying Shadow DOM within Stories Use shadow root queries directly in your `play()` function, like so: ```ts title="Button.stories.ts" const Story: Story = { play: async ({ canvas, userEvent }) => { const button = await canvas.findByShadowRole('button', { name: /Reset/i }); await userEvent.click(button); }, }; ``` Using `shadow-dom-testing-library` provides DOM querying methods that mirror the familiar API of `@testing-library/dom`, but they're able to traverse shadow roots. By extending Storybook's `canvas` object, you can access methods that help ensure your tests are clean, intuitive, and maintainable. ## Shadow DOM test Example Let's say you're testing a Web Component `` that renders shadow-root-contained checkboxes. ```tsx title="CheckboxGroup.stories.ts" // Replace the @storybook/web-components-vite package with @storybook/web-components if you're not using Storybook 9.0 import { Meta, StoryObj } from '@storybook/web-components-vite'; import { html } from 'lit'; /* * Replace the storybook/test import with `@storybook/test` and adjust the stories accordingly if you're not using Storybook 9.0. * Refer to the Storybook documentation for the correct package and imports for earlier versions. */ import { expect } from 'storybook/test'; import { Checkbox } from '../src/checkbox'; import { CheckboxGroup } from '../src/checkbox-group'; Checkbox.register(); CheckboxGroup.register(); const meta: Meta = { component: CheckboxGroup, title: 'Checkbox', }; export default meta; type Story = StoryObj; export const Required: Story = { render: () => html` Form label Checkbox option Checkbox option Checkbox option Checkbox option `, play: async ({ canvas, userEvent }) => { const checkboxes = await canvas.findAllByShadowRole('checkbox'); const firstCheckbox = checkboxes[0]; await userEvent.click(firstCheckbox); // select await userEvent.click(firstCheckbox); // deselect }, }; ``` Now you can simulate and test user interactions with deeply nested shadow elements from the `play()` function, without needing to manually reach into `shadowRoot`. --- ## Setup URL: https://chromatic.com/docs/playwright > Learn how to setup Chromatic for Playwright # Chromatic for Playwright Chromatic’s visual tests integrate with Playwright by extending Playwright’s `test` and `expect` utilities. That means you can transform your existing Playwright end-to-end (E2E) tests into visual regression tests with a single import change. Start capturing interactive snapshots within your Playwright E2E tests, and review visual changes in Chromatic’s cloud environment. ![Run Playwright tests and snapshot pages with Chromatic to detect visual bugs](../../images/workflow-playwright-visual-test.png) ## Why visual test with Playwright? Playwright enables you to write E2E tests that drive the browser to simulate and verify key user journeys like ‘sign up’ and ‘add to cart’. By snapshotting the UI states generated during your E2E tests, you can proactively catch visual bugs that might slip through traditional logic-based tests. ## How does Chromatic work? Chromatic works alongside your E2E tests. During your E2E test, Chromatic captures an [**archive**](/docs/faq/what-is-archive#what-is-an-archive) of each page and uploads it to Chromatic’s cloud. There, Chromatic generates snapshots and performs pixel diffing to identify any unintended visual changes. ### How does Chromatic’s visual testing differ from Playwright’s? While Playwright offers a basic ability to capture and visually compare screenshots of UI. Chromatic’s difference is that it provides a significantly more robust and developer-friendly visual testing solution: - **Robustness:** Chromatic captures full-page [archives](/docs/faq/what-is-archive) (including DOM, styling, and assets) of your test cases, which you can debug interactively in the Chromatic app, using browser dev tools. This eliminates the need to run further tests to troubleshoot errors. - **Workflow:** Chromatic removes the need to manage snapshots locally in your repo. Chromatic’s snapshots are indexed automatically, linked to git commits, and stored in the cloud for easy access. - **Parallelized testing:** Chromatic's cloud infrastructure automatically scales to run all tests simultaneously, eliminating the need for you to configure multiple workers on your CI. - **Dedicated review app:** Chromatic offers a suite of visual diffing tools to spot regressions fast. Features include unified and split diffs, highlighting ignored regions, spotlight mode to focus and zoom in on changes, and strobe diff to pinpoint subtle changes. ## Setup Chromatic for Playwright Chromatic supports Playwright version 1.38.0 and above. ### 1. Sign up and create a new project Generate a unique project token for your app by signing in to Chromatic and creating a project. Sign in with your GitHub, GitLab, Bitbucket, or email.
If your repository already has a Chromatic project linked to it, you can create an additional Chromatic project to run visual tests with Playwright. Follow the instructions for [sub-projects support](/docs/combine-stories-e2e).
![Setup project](../../images/setup-playwright.png) ### 2. Install Chromatic Install **[chromatic](https://www.npmjs.com/package/chromatic)** and `@chromatic-com/playwright` packages from npm. {/* prettier-ignore-start */} ```shell $ npm install --save-dev chromatic @chromatic-com/playwright ``` ```shell $ yarn add --dev chromatic @chromatic-com/playwright ``` ```shell $ pnpm add --dev chromatic @chromatic-com/playwright ``` {/* prettier-ignore-end */}
Chromatic only works with tests written in Playwright v1.12+.
### 3. Add Chromatic to Playwright tests Update your Playwright tests to use the Chromatic specific testing utilities from `@chromatic-com/playwright`. ```js import { test, expect } from '@playwright/test'; // [!code --] import { test, expect } from '@chromatic-com/playwright'; // [!code ++] // Then use as normal 👇 test('Homepage', async ({ page }) => { await page.goto('https://mealexpress.com/'); await expect(page).toHaveTitle('Mealdrop - find your next meal'); // ... }); ``` ### 4. Run Playwright Run your Playwright tests as you normally would. While your Playwright tests are running, Chromatic captures an [archive](/docs/faq/what-is-archive#what-is-an-archive) of your app’s UI for each test. {/* prettier-ignore-start */} ```shell $ npx playwright test ``` ```shell $ yarn playwright test ``` ```shell $ pnpm playwright test ``` {/* prettier-ignore-end */} ### 5. Run Chromatic Use your project token and run the following command in your project directory. {/* prettier-ignore-start */} ```shell $ npx chromatic --playwright -t= ``` ```shell $ yarn chromatic --playwright -t= ``` ```shell $ pnpm chromatic --playwright -t= ``` {/* prettier-ignore-end */} ### 6. Review changes When complete, you’ll see the build status and a link to review the changes. Click on that link to open Chromatic. ```shell ✔ Started build 1 → Continue setup at https://www.chromatic.com/setup?appId=... ✔ Build 1 auto-accepted → Tested X stories across 10 components; captured 10 snapshots in 1 minute 3 seconds ``` ![Chromatic build screen with a list of E2E tests that have visual changes](../../images/build-test-unreviewed-e2e.png) The build will be marked “unreviewed” and the changes will be listed in the “Tests” table. Go through each snapshot to review the diff and approve or reject the change. ![Clicking on a test takes you to the snapshot page where you can compare the new snapshot to the baseline](../../images/snapshot-unreviewed-e2e.png) Once you accept all changes, your build is marked as passed 🟢. This updates the baselines for those tests, ensuring future snapshots are compared against the latest approved version. ![Chromatic build screen with all changes approved](../../images/build-test-reviewed-e2e.png) --- ## Next: enhance your UI Testing workflow You're building robust components by uncovering bugs during development. Take your testing to the next level and safeguard against visual bugs by automating Chromatic whenever you push code. ![PR badge for UI Tests](../../images/prbadge-test.png) [**Integrate Chromatic into your CI pipeline**](/docs/ci) to get notified about any visual changes introduced by a pull request. Chromatic runs test any time you push code and reports changes via the “UI Tests” badge for your pull request. ### Advanced configuration options Take full control of your Chromatic and Playwright setup to match your team's specific workflows. Here are some powerful customizations we offer: - [**Take targeted snapshots:**](/docs/playwright/targeted-snapshots) Learn how to programmatically capture snapshots at specific points during your tests. - [**Fine-tune snapshot capture:**](/docs/playwright/configure#chromatic-options) Add delays before snapshots, modify the sensitivity threshold for change detection, and employ other granular controls. - [**Test for flexible user preferences:**](/docs/media-features#media-features) Ensure your UI gracefully adapts to user preferences like `forced-colors` and `prefers-reduced-motion`. - [**Responsive testing:**](/docs/viewports#playwright) Verify your UI's responsiveness across various screen sizes. Easily configure viewport dimensions in Playwright, both globally and within individual tests. - [**Manage sharded Playwright runs:**](/docs/playwright/sharding) If your Playwright test suite is distributed across multiple CI jobs, learn how to coordinate their completion before running Chromatic. --- ## Frequently asked questions
Can I use TurboSnap with Playwright visual tests? No. Playwright is a "black box" tool that tests your fully built app UI in a browser. TurboSnap is incompatible with this testing methodology because it requires tracking code changes and git history to identify the UI that's likely to change. We would love to integrate TurboSnap and Playwright but it isn't technically feasible right now.
Why is there a `Build your Storybook` step when running Playwright visual tests? Chromatic creates and runs a Storybook [archive](/docs/faq/what-is-archive) based on your Playwright project, so the build Storybook step is required. Chromatic doesn't run Playwright directly.
Can I use custom fixtures with Playwright visual tests? Chromatic’s Playwright integration is designed to work with third-party integrations and tools. It allows you to combine your existing fixtures via the [`mergeTests`](https://playwright.dev/docs/test-fixtures#combine-custom-fixtures-from-multiple-modules) option. See our FAQ on [BDD support with Playwright](/docs/faq/bdd-with-playwright) to learn more.
Is using Chrome mandatory when running Playwright with Chromatic? Yes. Chromatic relies on Chrome for snapshotting, so Chrome **must** be included in your Playwright configuration. Omitting it will trigger error: `Failed to run chromatic -playwright` Refer to Playwright documentation: [**Run tests on different browsers**](https://playwright.dev/docs/browsers#run-tests-on-different-browsers)
--- ## Configure URL: https://chromatic.com/docs/playwright/configure > Learn about the various configuration options for visual tests for Playwright # Configure visual tests for Playwright You can enhance your Playwright and Chromatic tests further by configuring them using the options outlined in the following sections. ## Playwright options The Chromatic [Playwright Fixture](https://playwright.dev/docs/test-fixtures) can be configured with `use` like all [Playwright options](https://playwright.dev/docs/test-use-options). You can set the available options globally in your Playwright configuration file as follows: ```ts title="playwright.config.ts" import { defineConfig } from "@playwright/test"; import { ChromaticConfig } from "@chromatic-com/playwright"; export default defineConfig({ use: { // 👇 Sets the option at the project level. disableAutoSnapshot: true, }, // Other project configuration options }); ``` ```js title="playwright.config.js" import { defineConfig } from "@playwright/test"; export default defineConfig({ use: { // 👇 Sets the option at the project level. disableAutoSnapshot: true, }, // Other project configuration options }); ``` You can also override them for specific tests by adding the [`test.use()`](https://playwright.dev/docs/api/class-test#test-use) method in your test file and provide the required options: ```ts title="tests/HomePage.spec.js|ts" test.describe('HomePage', () => { // 👇 Overrides the option in the test. test.use({ disableAutoSnapshot: true }); test('Loads the page with auto snapshotting disabled', async ({ page }) => { await page.goto('/'); }); }); ``` ## E2E options These options control how the Chromatic archive fixture behaves. | Option | Type | Description | | ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `disableAutoSnapshot` | `boolean` | When `true`, it will disable the snapshot that happens automatically at the end of a test when using the Chromatic test fixture. | | `cropToViewport ` | `boolean` | When `true`, the snapshot will be clipped to the height of the [viewport](/docs/viewports#playwright). | | `resourceArchiveTimeout` | `number` | Maximum amount of time each test will wait for the network to be idle while archiving resources. | | `assetDomains` | `array[string]` | A list of domains external to the test location that you want Chromatic to also capture assets from, e.g., ['other-domain.com','our-cdn.com']. | ## Chromatic options These options control how Chromatic behaves when capturing snapshots of your pages. | Option | Type | Chromatic Docs | | ------------------------- | --------------- | ----------------------------------------------------------------------------------- | | `delay` | `number` | [Delay](/docs/delay/) | | `diffIncludeAntiAliasing` | `boolean` | [Threshold for changes](/docs/threshold#anti-aliasing) | | `diffThreshold` | `number` | [Threshold for changes](/docs/threshold#setting-the-threshold) | | `ignoreSelectors` | `array[string]` | [Ignore elements](/docs/ignoring-elements#ignoring-elements-via-test-configuration) | | `forcedColors` | `string` | [Media Features](/docs/media-features#test-high-contrast-color-schemes) | | `pauseAnimationAtEnd` | `boolean` | [Animations](/docs/animations#css-animations) | | `prefersReducedMotion` | `string` | [Media Features](/docs/media-features#verify-reduced-motion-animations) | ## Environment variables Some options can be configured through environment variables. | Environment variable | Description | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CHROMATIC_ARCHIVE_LOCATION` | If you have configured your project's [`outputDir`](https://playwright.dev/docs/api/class-testproject#test-project-output-dir) option to be different than the default, you must set the `CHROMATIC_ARCHIVE_LOCATION` environment variable to the same value. This ensures that the Chromatic can find the archives generated by Playwright tests. | --- ## Working in Monorepos Often, when using a monorepo, developers tend to keep their e2e tests in a subdirectory instead of in the root of the project. At the same time, the Storybook and Chromatic configuration details live at the project’s root. In these cases, you will need to update the `archive-storybook` and `build-archive-storybook` scripts in your `package.json` by setting the [`-c` flag](https://storybook.js.org/docs/api/cli-options#build) and `CHROMATIC_ARCHIVE_LOCATION` environment variable. For example: ```json title="package.json" "scripts": { "archive-storybook": "CHROMATIC_ARCHIVE_LOCATION=path/to/test-results archive-storybook -c path/to/node_modules/@chromaui/archive-storybook/config", "build-archive-storybook": "CHROMATIC_ARCHIVE_LOCATION=path/to/test-results build-archive-storybook -c path/to/node_modules/@chromaui/archive-storybook/config" } ```
💡 For additional information on using Chromatic with a monorepo, see our [monorepo documentation](/docs/monorepos).
--- ## Sharding URL: https://chromatic.com/docs/playwright/sharding > Learn how to run Playwright tests in parallel across shared CI jobs # Sharded Playwright Runs When running your Playwright tests over multiple shared CI jobs, you'll need to wait for all jobs to complete, ensuring the results are saved in either the default test results directory or a custom directory accessible by the next job as artifacts. You can then run Chromatic in a job that depends on all the shards. ## GitHub Actions If you're working with GitHub Actions, you can configure a job [matrix](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs) to run Playwright tests in parallel across multiple instances. Enabling this option will run a separate job for every combination of the provided values, merge the test results as a single artifact, and make them available to the Chromatic job when it runs. ```yaml title=".github/workflows/chromatic.yml" name: 'UI Tests' on: push jobs: playwright: name: Run Playwright strategy: matrix: shard: [1, 2] runs-on: ubuntu-latest container: image: mcr.microsoft.com/playwright:v1.62.0-noble steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: actions/setup-node@v7 with: node-version: 24.19.0 - name: Install dependencies run: npm ci - name: Run Playwright tests run: npx playwright test --shard=${{ matrix.shard }}/${{ strategy.job-total }} env: HOME: /root - uses: actions/upload-artifact@v7 if: always() with: name: playwright-report-${{ matrix.shard }}_${{ strategy.job-total }} path: ./test-results/chromatic-archives retention-days: 30 chromatic: name: Run Chromatic needs: playwright runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: actions/setup-node@v7 with: node-version: 24.19.0 - name: Install dependencies run: npm ci - name: Download all workflow run artifacts uses: actions/download-artifact@v8 with: path: ./test-results/chromatic-archives pattern: playwright-report-* merge-multiple: true - name: Run Chromatic tests uses: chromaui/action@latest with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} playwright: true ``` ## GitLab Pipelines To run Playwright tests in parallel across shared CI jobs in GitLab, you can use the [`parallel`](https://docs.gitlab.com/ee/ci/yaml/index.html#parallel) option in your GitLab CI workflow. The job will be split into multiple smaller jobs running in parallel sequentially named based on the values of the environment variables. The results will be saved as an artifact and accessible by the Chromatic job when it runs. ```yaml title=".gitlab-ci.yml" image: node:krypton stages: - UI_Tests cache: key: $CI_COMMIT_REF_SLUG-$CI_PROJECT_DIR paths: - .npm/ before_script: - npm ci Playwright: stage: UI_Tests needs: [] image: mcr.microsoft.com/playwright:v1.62.0-noble parallel: 2 script: - npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL allow_failure: true when: always paths: - "test-results/" expire_in: 4 weeks Chromatic: stage: UI_Tests needs: [Playwright] script: - npx chromatic --playwright --project-token=$CHROMATIC_PROJECT_TOKEN ``` ## CircleCI To run Playwright tests in parallel across shared CI jobs in CircleCI, you can use the `parallelism` option in your CircleCI workflow to set the number of parallel jobs to run. You'll also need to override the default parallelization environment variables to allow the Playwright test runner to split the tests across the instances. When finished, the test results will be saved as an artifact and accessible by the Chromatic job when it runs. ```yaml title=".circleci/config.yml" version: 2.1 executors: pw-noble-development: docker: - image: mcr.microsoft.com/playwright:v1.62.0-noble chromatic-ui-testing: docker: - image: cimg/node:24.19.0 jobs: Playwright: executor: pw-noble-development parallelism: 2 working_directory: ~/repo steps: - checkout - restore_cache: name: Restore NPM cache keys: - v1-dependencies-{{ checksum "package-lock.json" }} - v1-dependencies- - run: name: 'Install Playwright dependencies' command: npm ci - run: name: 'Run Playwright tests' command: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test --shard=${SHARD}/${CIRCLE_NODE_TOTAL} when: always - store_artifacts: path: ./test-results - persist_to_workspace: root: . paths: - test-results Chromatic: executor: chromatic-ui-testing working_directory: ~/repo steps: - checkout - restore_cache: name: Restore NPM cache keys: - v1-dependencies-{{ checksum "package-lock.json" }} - v1-dependencies- - run: npm ci - attach_workspace: at: . - run: name: 'Run Chromatic' command: npx chromatic --playwright --project-token=${CHROMATIC_PROJECT_TOKEN} workflows: UI_Tests: jobs: - Playwright - Chromatic: requires: - Playwright ``` ## Jenkins If you're working with Jenkins, you can configure your pipeline to run Playwright tests in parallel, distributed across multiple stages, save the test results as artifacts, and run Chromatic in a separate stage that depends on the test results from the previous job. ```groovy title="Jenkinsfile" pipeline { agent any tools {nodejs "node"} stages { stage('Install dependencies') { steps { sh 'npm ci' } } stage('Playwright'){ environment { MAX_SHARDS = '2' } parallel { stage('Shard #1') { agent { docker { image 'mcr.microsoft.com/playwright:v1.62.0-noble' reuseNode true } } environment { SHARD = '1' } steps { sh 'npm ci' sh "npx playwright test --shard=${SHARD}/${env.MAX_SHARDS}" } post { always { archiveArtifacts 'test-results/**' } } } stage('Shard #2') { agent { docker { image 'mcr.microsoft.com/playwright:v1.62.0-noble' reuseNode true } } environment { SHARD = '2' } steps { sh 'npm ci' sh "npx playwright test --shard=${SHARD}/${env.MAX_SHARDS}" } post { always { archiveArtifacts 'test-results/**' } } } } } stage('Chromatic') { environment { CHROMATIC_PROJECT_TOKEN = credentials('chromatic-project-token') } steps { sh "npx chromatic --playwright" } } } } ``` ## Semaphore To run Playwright tests in parallel across shared CI jobs in Semaphore, you can use the [`parallelism`](https://docs.semaphore.io/reference/pipeline-yaml#parallelism-in-jobs) option in your workflow. The job will be split into multiple smaller jobs running in parallel sequentially named based on the values of the environment variables. The results will be saved as an artifact and accessible by the Chromatic job when it runs. ```yml title=".semaphore/semaphore.yml" version: v1.0 name: UI Tests agent: machine: type: e2-standard-2 os_image: ubuntu2404 global_job_config: prologue: commands: - checkout blocks: - name: Playwright dependencies: [] task: agent: machine: type: e2-standard-2 os_image: ubuntu2404 containers: - name: Plawyright image: mcr.microsoft.com/playwright:v1.62.0-noble jobs: - name: Run Playwright commands: - cache restore npm-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) - npm ci - cache store npm-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) ~/.npm - npx playwright test --shard=$SEMAPHORE_JOB_INDEX/$SEMAPHORE_JOB_COUNT parallelism: 2 epilogue: always: commands: - artifact push workflow --force test-results - name: Run Chromatic dependencies: ['Playwright'] task: prologue: commands: - sem-version node 24.19.0 - artifact pull workflow test-results secrets: - name: CHROMATIC_PROJECT_TOKEN jobs: - name: Chromatic commands: - cache restore npm-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) - npm ci - npx chromatic --playwright ``` ## Other CI providers If you’re using a different CI provider, you’ll need to adapt your workflow to run Playwright tests in parallel across shared CI jobs and enable Chromatic to run after all instances have finished. Here’s an example of how you might do this in a generic CI provider. ```yml title="your-workflow.yml" image: node:krypton - run: name: "Playwright" displayName: "Run Playwright tests" container: mcr.microsoft.com/playwright:v1.62.0-noble options: parallel: 2 artifacts: - test-results/** command: npx playwright test --shard=$CI_JOB_INDEX/CI_TOTAL_JOBS - run: name: "Chromatic" displayName: "Run Chromatic" requires: [Playwright] command: npx chromatic --playwright --project-token=$CHROMATIC_PROJECT_TOKEN ``` --- ## Targeted snapshots URL: https://chromatic.com/docs/playwright/targeted-snapshots > Learn how to capture snapshots at specific points during your Playwright tests programmatically # Take targeted snapshots with Playwright By default, Chromatic takes a snapshot at the end of every Playwright test, whether it passes or fails. However, you can also choose to programmatically take snapshots at specific points in your tests using the `takeSnapshot` function inside your test runs. `takeSnapshot` is especially useful for capturing a snapshot of your UI’s appearance when your UI reaches a specific state mid-test: ```js title="tests/Example.spec.js|ts" import { test, expect, takeSnapshot } from '@chromatic-com/playwright'; // 👇 Add testInfo parameter test('Can filter product', async ({ page }, testInfo) => { await page.goto('/restaurant/dp/B07KMG72'); await page.locator('.menu__item:first-of-type').click(); // Call takeSnapshot to take an archive "snapshot" // of the page at this point in the test. // 👇 Pass testInfo to takeSnapshot await takeSnapshot(page, testInfo); // [!code highlight] // Continue with the test. await page.getByRole('link', { name: 'Add to cart' }).click(); // You can call takeSnapshot multiple times if necessary. // To help disambiguate, you can give the snapshot a name, // which is passed as the second argument to takeSnapshot. await takeSnapshot(page, 'After adding to cart', testInfo); // [!code highlight] await expect(page).toHaveTitle(/Cart/); }); ``` --- ## Setup URL: https://chromatic.com/docs/cypress > Learn how to setup Chromatic for Cypress # Chromatic for Cypress Chromatic’s visual tests integrate with Cypress via a plugin. This means you can transform your existing Cypress end-to-end (E2E) into visual regression tests with a single import change. Start capturing interactive snapshots while Cypress E2E tests run, and review visual changes in Chromatic’s cloud environment. ![Run Cypress tests and snapshot pages with Chromatic to detect visual bugs](../../images/workflow-cypress-visual-test.png) ## Why visual test with Cypress? Cypress enables you to write E2E tests that drive the browser to simulate key user flows through your application, like ‘sign up’ and ‘add to cart’. By snapshotting the UI states generated during your E2E tests, you can proactively catch visual bugs that might slip through traditional logic-based tests. ## How does Chromatic work? Chromatic communicates with Cypress via the devtools protocol. While your Cypress E2E tests run, Chromatic captures an [**archive**](/docs/faq/what-is-archive) of the page and uploads it to the cloud. There, Chromatic generates snapshots and performs pixel diffing to identify any unintended visual changes. Key advantages of Chromatic's Cypress integration: - **Robustness:** Chromatic saves full page [archives](/docs/faq/what-is-archive) (DOM, styling, assets) of every test case. This lets you interactively debug the UI within the Chromatic app, eliminating the need to run tests locally for troubleshooting. - **Workflow:** Chromatic streamlines visual testing by removing the need to manually manage screenshots in your repo. Snapshots are indexed, linked to git commits, and stored in the cloud for easy access. - **Parallelized testing:** Chromatic's cloud infrastructure automatically scales to run all tests simultaneously, eliminating the need for you to configure multiple workers on your CI. - **Dedicated review app:** Chromatic offers a suite of visual diffing tools to spot regressions fast. Features include unified and split diffs, highlighting ignored regions, spotlight mode to focus and zoom in on changes, and strobe diff to pinpoint subtle changes. ## Setup Chromatic for Cypress Chromatic supports Cypress version 13.5.0 and above. ### 1. Sign up and create a new project Generate a unique project token for your app by signing in to Chromatic and creating a project. Sign in with your GitHub, GitLab, Bitbucket, or email.
If your repository already has a Chromatic project linked to it, you can create an additional Chromatic project to run visual tests with Cypress. Follow the instructions for [sub-projects support](/docs/combine-stories-e2e).
![Setup project](../../images/setup-cypress.png) ### 2. Install Chromatic Install [chromatic](https://www.npmjs.com/package/chromatic) and `@chromatic-com/cypress` packages from npm. {/* prettier-ignore-start */} ```shell $ npm install --save-dev chromatic @chromatic-com/cypress ``` ```shell $ yarn add --dev chromatic @chromatic-com/cypress ``` ```shell $ pnpm add --dev chromatic @chromatic-com/cypress ``` {/* prettier-ignore-end */} ### 3. Add Chromatic to Cypress tests Add the following to your `cypress/support/e2e.js` file: ```js title="cypress/support/e2e.js" import '@chromatic-com/cypress/support'; ``` Then, install the Chromatic plugin in your `cypress.config.js` file: ```js title="cypress.config.js" const { defineConfig } = require('cypress'); const { installPlugin } = require('@chromatic-com/cypress'); module.exports = defineConfig({ e2e: { setupNodeEvents(on, config) { installPlugin(on, config); }, }, }); ``` ### 4. Run Cypress Prefix your Cypress command with the following environment variable. This enables Chromatic to communicate with Cypress using the Chrome DevTools Protocol. Then run your Cypress tests as you normally would. {/* prettier-ignore-start */} ```shell $ ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=9222 npx cypress run ``` ```shell $ ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=9222 yarn cypress run ``` ```shell $ ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=9222 pnpm cypress run ``` {/* prettier-ignore-end */} ### 5. Run Chromatic Use your project token and run the following command in your project directory. ```shell npx chromatic --cypress -t= ``` ### 6. Review changes When complete, you’ll see the build status and a link to review the changes. Click on that link to open Chromatic. ```shell ✔ Started build 1 → Continue setup at https://www.chromatic.com/setup?appId=... ✔ Build 1 auto-accepted → Tested X stories across 10 components; captured 10 snapshots in 1 minute 3 seconds ``` ![Chromatic build screen with a list of E2E tests that have visual changes](../../images/build-test-unreviewed-e2e.png) The build will be marked “unreviewed” and the changes will be listed in the “Tests” table. Go through each snapshot to review the diff and approve or reject the change. ![Clicking on a test takes you to the snapshot page where you can compare the new snapshot to the baseline](../../images/snapshot-unreviewed-e2e.png) Once you accept all changes, your build is marked as passed 🟢. This updates the baselines for those tests, ensuring future snapshots are compared against the latest approved version. ![Chromatic build screen with all changes approved](../../images/build-test-reviewed-e2e.png) --- ## Next: enhance your UI Testing workflow You're building robust components by uncovering bugs during development. Take your testing to the next level and safeguard against visual bugs by automating Chromatic whenever you push code. ![PR badge for UI Tests](../../images/prbadge-test.png) [**Integrate Chromatic into your CI pipeline**](/docs/ci) to get notified about any visual changes introduced by a pull request. Chromatic runs test any time you push code and reports changes via the “UI Tests” badge for your pull request. ### Advanced configuration options Take full control of your Chromatic and Cypress setup to match your team's specific workflows. Here are some powerful customizations we offer: - [**Take targeted snapshots:**](/docs/cypress/targeted-snapshots) Learn how to programmatically capture snapshots at specific points during your tests. - [**Fine-tune snapshot capture:**](/docs/cypress/configure#chromatic-options) Add delays before snapshots, modify the sensitivity threshold for change detection, and employ other granular controls. - [**Test for flexible user preferences:**](/docs/media-features#media-features) Ensure your UI gracefully adapts to user preferences like `forced-colors` and `prefers-reduced-motion`. - [**Responsive testing:**](/docs/viewports#cypress) Verify your UI's responsiveness across various screen sizes. Easily configure viewport dimensions in Cypress, both globally and within individual tests. --- ## Frequently asked questions
Can I use TurboSnap with Cypress visual tests? No. Cypress is a "black box" tool that tests your fully built app UI in a browser. TurboSnap is incompatible with this testing methodology because it requires tracking code changes and git history to identify the UI that's likely to change. We would love to integrate TurboSnap and Cypress but it isn't technically feasible right now.
Why is there a `Build your Storybook` step when running Cypress visual tests? Chromatic creates and runs a Storybook [archive](/docs/faq/what-is-archive) based on your Cypress project, so the build Storybook step is required. Chromatic doesn't run Cypress directly.
Is using Chrome mandatory when running Cypress with Chromatic? Yes. Chromatic relies on Chrome for snapshotting, so Chrome **must** be included in your Cypress configuration. Omitting it will trigger error: `Failed to run chromatic -cypress` Refer to Cypress documentation: [**Cross Browser Testing**](https://docs.cypress.io/app/guides/cross-browser-testing)
--- ## Configure URL: https://chromatic.com/docs/cypress/configure > Learn about the various configuration options for visual tests for Cypress # Configure visual tests for Cypress You can enhance your Cypress and Chromatic tests further by configuring them using the options outlined in the following sections. ## Cypress options Cypress can be configured with [Cypress environment variables](https://docs.cypress.io/app/guides/environment-variables). You can set the available options globally in your Cypress configuration file as follows: ```ts title="cypress.config.js|ts" export default defineConfig({ env: { // 👇 Sets the option at the project level. disableAutoSnapshot: true, }, // Other project configuration options }); ``` You can also override them for specific tests using via the [`env`](https://docs.cypress.io/app/references/configuration#Suite-configuration) option in the test configuration: ```ts title="cypress/e2e/HomePage.cy.js|ts" describe('HomePage', () => { it( 'Loads the page with auto snapshotting disabled', { env: { // 👇 Overrides the option in the test. disableAutoSnapshot: true, }, }, () => { cy.visit('/'); } ); }); ``` ### E2E options These options control how the Chromatic archive fixture behaves. | Option | Type | Description | | ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `disableAutoSnapshot` | `boolean` | When `true`, it will disable the snapshot that happens automatically at the end of a test when using the Chromatic test fixture. | | `resourceArchiveTimeout` | `number` | Maximum amount of time each test will wait for the network to be idle while archiving resources. | | `assetDomains` | `array[string]` | A list of domains external to the test location that you want Chromatic to also capture assets from, e.g., ['other-domain.com','our-cdn.com']. | ### Chromatic options These options control how Chromatic behaves when capturing snapshots of your pages. | Option | Type | Chromatic Docs | | ------------------------- | --------------- | ----------------------------------------------------------------------------------- | | `delay` | `number` | [Delay](/docs/delay/) | | `diffIncludeAntiAliasing` | `boolean` | [Threshold for changes](/docs/threshold#anti-aliasing) | | `diffThreshold` | `number` | [Threshold for changes](/docs/threshold#setting-the-threshold) | | `ignoreSelectors` | `array[string]` | [Ignore elements](/docs/ignoring-elements#ignoring-elements-via-test-configuration) | | `forcedColors` | `string` | [Media Features](/docs/media-features#test-high-contrast-color-schemes) | | `pauseAnimationAtEnd` | `boolean` | [Animations](/docs/animations#css-animations) | | `prefersReducedMotion` | `string` | [Media Features](/docs/media-features#verify-reduced-motion-animations) | ### Environment variables Some options can be configured through environment variables. | Environment variable | Description | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CHROMATIC_ARCHIVE_LOCATION` | If you have configured your project's [`downloadsFolder`](https://docs.cypress.io/guides/references/configuration#Downloads) option to be different than the default, you must set the `CHROMATIC_ARCHIVE_LOCATION` environment variable to the same value. This ensures that the Chromatic can find the archives generated by Cypress tests. | --- ## Targeted snapshots URL: https://chromatic.com/docs/cypress/targeted-snapshots > Learn how to capture snapshots at specific points during your Cypress tests programmatically # Take targeted snapshots with Cypress By default, Chromatic takes a snapshot at the end of every Cypress test, whether it passes or fails. However, you can also choose to programmatically take snapshots at specific points in your tests using the `takeSnapshot` function inside your test runs. `takeSnapshot` is especially useful for capturing a snapshot of your UI’s appearance when your UI reaches a specific state mid-test: ```js title="cypress/e2e/Example.cy.js|ts" describe('My First Test', () => { it('Visits the Kitchen Sink', () => { // 👇 Navigate to target page cy.visit('https://example.cypress.io'); // 📸 Tell Chromatic to take a snapshot of the initial page state cy.takeSnapshot(); // [!code highlight] // 👇 Finish the test by opening the dropdown menu cy.get('.dropdown:first-of-type > .dropdown-toggle').click(); // You can call takeSnapshot multiple times if necessary. // To help disambiguate, you can give the snapshot a name, // which is passed as an argument to cy.takeSnapshot. cy.takeSnapshot('After opening dropdown'); // [!code highlight] cy.contains('Files').click(); // 📸 Chromatic automatically takes a snapshot here, at the end of the test. }); }); ``` --- ## Setup URL: https://chromatic.com/docs/vitest > Learn how to setup Chromatic for Vitest # Chromatic for Vitest (beta) Chromatic’s visual tests integrate with Vitest via a plugin. This means you can transform your existing Vitest Browser Mode tests into visual regression tests with a minimal configuration file change. Start capturing interactive snapshots while Vitest tests run, and review visual changes in Chromatic’s cloud environment. ![Run Vitest tests and snapshot pages with Chromatic to detect visual bugs](../../images/workflow-vitest-visual-test.png)
🚧 **Early access:** Chromatic for Vitest is currently in early access. If you’re interested in trying it out, [request access here](https://share.hsforms.com/1jy-S7e-8TLWGnI_dT7T9Uwr5etx?ref=chromaticblog.ghost.io&__hstc=243929690.cdaecf856b9dae7a6940d569e83ab885.1777500929046.1780520573197.1780527451431.43&__hssc=243929690.1.1780527451431&__hsfp=38ac06b2498e4056a6274f360c3c04aa).
## Why visual test with Vitest? Vitest Browser Mode enables you to write component tests that drive the browser to simulate and verify key user interactions like ‘toggle accordion’ and ‘filter search results’. By snapshotting the UI states generated during your component tests, you can proactively catch visual bugs that might slip through traditional logic-based tests. ## How does Chromatic work? Chromatic works alongside your Browser Mode component tests. During your test run, Chromatic captures an [**archive**](/docs/faq/what-is-archive#what-is-an-archive) of each test case and uploads it to Chromatic’s cloud. There, Chromatic generates snapshots and performs pixel diffing to identify any unintended visual changes. ### How does Chromatic’s visual testing differ from Vitest’s? While Vitest offers a basic ability to capture and visually compare screenshots of UI. Chromatic’s difference is that it provides a significantly more robust and developer-friendly visual testing solution: - **Robustness:** Chromatic captures [archives](/docs/faq/what-is-archive) (including DOM, styling, and assets) of your test cases, which you can debug interactively in the Chromatic app, using browser dev tools. This eliminates the need to run further tests to troubleshoot errors. - **Workflow:** Chromatic removes the need to manage snapshots locally in your repo. Chromatic’s snapshots are indexed automatically, linked to git commits, and stored in the cloud for easy access. - **Parallelized testing:** Chromatic's cloud infrastructure automatically scales to run all tests simultaneously, eliminating the need for you to configure multiple workers on your CI. - **Dedicated review app:** Chromatic offers a suite of visual diffing tools to spot regressions fast. Features include unified and split diffs, highlighting ignored regions, spotlight mode to focus and zoom in on changes, and strobe diff to pinpoint subtle changes. ## Requirements - Vitest version 4.0.0 and above - Vitest project must use [@vitest/browser-playwright](https://vitest.dev/config/browser/playwright) ## Set up Chromatic for Vitest ### 1. Sign up and create a new project Create a Chromatic account and/or sign in with your GitHub, GitLab, Bitbucket, or email. Then reach out to your point of contact at Chromatic to enable Vitest support for your account. Generate a unique project token for your app by creating a project.
If your repository already has a Chromatic project linked to it, you can create an additional Chromatic project to run visual tests with Vitest. Follow the instructions for [sub-projects support](/docs/monorepos#run-chromatic-for-each-subproject).
![Set up project](../../images/setup-vitest.png) ### 2. Install Chromatic Install **[`chromatic`](https://www.npmjs.com/package/chromatic)** and **[`@chromatic-com/vitest`](https://www.npmjs.com/package/@chromatic-com/vitest)** packages from npm. {/* prettier-ignore-start */} ```shell $ npm install --save-dev chromatic @chromatic-com/vitest ``` ```shell $ yarn add --dev chromatic @chromatic-com/vitest ``` ```shell $ pnpm add --dev chromatic @chromatic-com/vitest ``` {/* prettier-ignore-end */} ### 3. Add Chromatic to Vitest tests Add `chromaticPlugin` in your [Vitest configuration file](https://vitest.dev/config/). ```ts title="vitest.config.ts" import { defineConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import { chromaticPlugin } from '@chromatic-com/vitest/plugin'; // [!code ++] export default defineConfig({ plugins: [chromaticPlugin()], // [!code ++] test: { // Your existing browser mode configuration, https://vitest.dev/guide/browser/#configuration browser: { provider: playwright(), enabled: true, instances: [{ browser: 'chromium' }], }, }, }); ``` If you are using browser mode in a single [Vitest project](https://vitest.dev/guide/projects.html), you can apply the plugin on project level: ```ts title="vitest.config.ts" import { defineConfig } from "vitest/config"; import { chromaticPlugin } from "@chromatic-com/vitest/plugin"; // [!code ++] export default defineConfig({ test: { projects: [ { test: { name: "Unit Tests", include: ["**/*.test.ts"], }, }, // Option 1: For all browser tests { plugins: [chromaticPlugin()], // [!code ++] test: { name: "Browser Tests", include: ["**/*.test.tsx"], browser: { ... } }, }, // Option 2: For tests ending with "*.visual.test.tsx" only { // [!code ++] plugins: [chromaticPlugin()], // [!code ++] test: { // [!code ++] name: "Visual Regression", // [!code ++] include: ["**/*.visual.test.tsx"], // [!code ++] browser: { ... }, // [!code ++] }, // [!code ++] }, // [!code ++] ], }, }); ``` ### 4. Run Vitest Run your Vitest tests as you normally would. While your Vitest tests are running, Chromatic captures an [archive](/docs/faq/what-is-archive) of your app’s UI for each test. {/* prettier-ignore-start */} ```shell $ npx vitest ``` ```shell $ yarn vitest ``` ```shell $ pnpm vitest ``` {/* prettier-ignore-end */} ### 5. Run Chromatic Use your project token and run the following command in your project directory. {/* prettier-ignore-start */} ```shell $ npx chromatic --vitest -t= ``` ```shell $ yarn chromatic --vitest -t= ``` ```shell $ pnpm chromatic --vitest -t= ``` {/* prettier-ignore-end */} ### 6. Review changes When complete, you’ll see the build status and a link to review the changes. Click on that link to open Chromatic. ```shell ✔ Started build 1 → Continue setup at https://www.chromatic.com/setup?appId=... ✔ Build 1 auto-accepted → Tested X stories across 10 components; captured 10 snapshots in 1 minute 3 seconds ``` ![Chromatic build screen with a list of component tests that have visual changes](../../images/build-test-unreviewed-vitest.png)
How can I change the name of a test? By default, the name and surrounding hierarchy of a test in a Chromatic build is generated based on the file path, suite name(s), and test name in Vitest. Sometimes, the file path and component result in a less than helpful display in the build table. For example, you could have test files located at `src/pages/dashboard/Page.test.tsx` and `src/pages/settings/Page.test.tsx`, which would result in both tests being displayed as `Page` in the build table, making it difficult to differentiate between them: ![Chromatic build screen with two sets of tests both labeled "Page"](../../images/vitest-buildscreen-name-incorrect.png) You can customize this name by calling `configure` function from `@chromatic-com/vitest` with the [`title` option](/docs/vitest/configure#test-run-options) in the test file. ```jsx title="src/pages/dashboard/Page.test.tsx" import { describe, test } from 'vitest'; import { configure } from '@chromatic-com/vitest'; // [!code ++] import { Page } from './Page'; configure({ title: 'Dashboard' }); // [!code ++] test('Dashboard', async () => { await render(); }); ``` Which displays like this in the build table: ![Chromatic build screen with "Dashboard" and "Settings" test sets](../../images/vitest-buildscreen-name-correct.png)
The build will be marked “unreviewed” and the changes will be listed in the “Tests” table. Go through each snapshot to review the diff and approve or reject the change. ![Homepage : Can open product / tablet. Comparing the baseline snapshot to the new one.](../../images/snapshot-unreviewed-vitest.png) Once you accept all changes, your build is marked as passed 🟢. This updates the baselines for those tests, ensuring future snapshots are compared against the latest approved version. ![Chromatic build screen with all changes approved](../../images/build-test-reviewed-vitest.png) --- ## Next: enhance your UI Testing workflow You're building robust components by uncovering bugs during development. Take your testing to the next level and safeguard against visual bugs by automating Chromatic whenever you push code. ![PR badge for UI Tests](../../images/prbadge-test.png) [**Integrate Chromatic into your CI pipeline**](/docs/ci) to get notified about any visual changes introduced by a pull request. Chromatic runs test any time you push code and reports changes via the “UI Tests” badge for your pull request. ### Advanced configuration options Take full control of your Chromatic and Vitest setup to match your team's specific workflows. Here are some powerful customizations we offer: - [**Take targeted snapshots:**](/docs/vitest/targeted-snapshots) Learn how to programmatically capture snapshots at specific points during your tests. - [**Fine-tune snapshot capture:**](/docs/vitest/configure#snapshot-options) Add delays before snapshots, modify the sensitivity threshold for change detection, and employ other granular controls. - [**Test for flexible user preferences:**](/docs/media-features#media-features) Ensure your UI gracefully adapts to user preferences like `forced-colors` and `prefers-reduced-motion`. - [**Manage sharded Vitest runs:**](/docs/vitest/sharding) If your Vitest test suite is distributed across multiple CI jobs, learn how to coordinate their completion before running Chromatic. --- ## Frequently asked questions
How can I disable automatic snapshots for a test? By default, Chromatic captures a snapshot at the end of each test. You can disable this behavior for specific test cases, a suite of tests, test files, or the entire project using the [`disableAutoSnapshot` option](/docs/vitest/configure). You can also make automatic snapshots opt-in instead of opt-out, by disabling them at the project level and then re-enabling them for specific test files, suites, or test cases. Regardless of whether a test has automatic snapshots enabled or disabled, you can always capture a snapshot programmatically at any point during the test using the [`takeSnapshot` function](/docs/vitest/targeted-snapshots).
Can I use TurboSnap with Vitest visual tests? Yes. TurboSnap is supported. See the [dedicated guide](/docs/vitest/turbosnap) for more information.
Why is there a `Build your Storybook` step when running Vitest visual tests? Chromatic creates and runs a Storybook [archive](/docs/faq/what-is-archive) based on your Vitest project, so the build Storybook step is required. Chromatic doesn't run Vitest directly.
Is using `@vitest/browser-playwright` with Chromium based browsers mandatory when running Vitest with Chromatic? Yes. Chromatic relies on Chromium for snapshotting, so Chromium based browsers **must** be included in your Vitest configuration. Omitting it will trigger error: `Failed to run chromatic --vitest` If you have use case for other browser providers, feel free to [send a feature request](https://github.com/chromaui/chromatic-e2e/issues/new/choose). Refer to Vitest documentation: [**Vitest | Browser Mode**](https://vitest.dev/guide/browser)
--- ## Configure URL: https://chromatic.com/docs/vitest/configure > Learn about the various configuration options for visual tests for Vitest # Configure visual tests for Vitest You can enhance your Vitest and Chromatic tests further by configuring these options globally in your [Vitest configuration file](https://vitest.dev/config/). ```ts title="vitest.config.ts" import { defineProject } from 'vitest/config'; import { chromaticPlugin } from '@chromatic-com/vitest/plugin'; export default defineProject({ plugins: [ chromaticPlugin({ // 👇 Global options placed here disableAutoSnapshot: true, }), ], // ...Other Vitest configuration }); ``` In addition to global configuration, you can also configure options at the test file, suite, and test case level. This allows you to have more granular control over which tests are snapshotted and how they are snapshotted.
When called within `test()`, it configures options for that test: ```jsx title="test/accordion.test.tsx" import { test } from 'vitest'; import { configure } from '@chromatic-com/vitest'; test('accordion', async () => { // ❌ Not snapshotted configure({ disableAutoSnapshot: true }); // Scopes to this test only await render(); }); test('button', async () => { // ✅ Snapshotted automatically await render( ``` When you apply `display: flex` to a parent container (the ` ```
Why isn’t my portal component (modal, dialog, popover, tooltip, or menu) captured? Or why is its snapshot cut off? If you use an `animateIn` effect, set a [delay](/docs/delay) so Chromatic captures the snapshot after the animation completes. Your component might be rendering outside of the viewport. Either reposition the component or adjust the [viewport size](/docs/modes/viewports/).
### Unexpected rendering behavior
Do you support taking snapshots of a component with multiple themes? Yes. Chromatic's [Modes](/docs/modes) simplify visual testing for stories with different global configurations, such as themes. Follow the [Themes guide](/docs/themes) to get started.
Why are stories with mocked data not being snapshotted correctly? Chromatic snapshots sometimes show the initial or intermediate loading state of the UI, instead of the final state with the mocked data. This can lead to visual tests failing, even though the Storybook renders correctly locally. To debug this issue, follow these steps: 1. Ensure that MSW (Mock Service Worker) is correctly initialized by following the [MSW Storybook add-on setup instructions](https://github.com/mswjs/msw-storybook-addon?tab=readme-ov-file#configure-the-addon). 2. Confirm that you're not using outdated versions of `msw`, `msw-storybook-addon`, or any other community add-ons. 3. Run `npm run build-storybook` and `npx http-server storybook-static -o` locally to check the console and address any MSW warnings or errors. A setup that works locally can still fail in Chromatic. 4. Pay close attention to how you define MSW handlers, especially for API requests with query parameters. MSW recommends matching only the path in the handler URL and accessing query parameters inside the resolver function using `req.url.searchParams.get()`. 5. Ensure all necessary assets, such as CSS files, load correctly in your stories. Consider preloading them in [`.storybook/preview-head.html`](https://storybook.js.org/docs/configure/story-rendering#adding-to-head). 6. Add an [interaction test](/docs/interactions) to your story to assert that mocked data is present and the component is in the expected state before the test concludes. 7. Use [delays](/docs/delay) to ensure that mocked data is fully available before Chromatic takes a snapshot.
Why am I seeing a blank snapshot? Blank snapshots are often caused by: - **An `animateIn` effect**—If your component uses an `animateIn` effect, [set a delay](/docs/delay) so Chromatic captures the snapshot after the animation completes. - **`position: fixed`**—Fixed-position elements may depend on viewport size but don't have dimensions themselves. Wrap your component in an element with defined height and width. Learn how to [improve test stability](#improve-test-stability).
Why are ignored elements still causing diffs? By default, Chromatic's diffing algorithm skips the DOM elements marked with either a `.chromatic-ignore` CSS class or `data-chromatic="ignore"` attribute. If you're using this functionality but incoming changes are still captured, ensure that both the [baseline](/docs/branching-and-baselines) and new snapshots retain the same dimensions, including width, height, and relative positioning.
Why does my tab component make a test unstable? Certain UI libraries like Material calculate the dimensions of each tab by measuring the rendered width of the tab's children using JavaScript (for example, via `getBoundingClientRect()`). However, loading a custom font can make the test unstable. Fonts affect the dimensions of text within tabs. Because custom fonts can load before, during, or after the tab component, the dimensions calculated by the component can vary across runs. The solution we recommend is to use a `` in your [`.storybook/preview-head.html`](https://storybook.js.org/docs/configure/story-rendering#adding-to-head) to preload the font before the story renders. This ensures that the dimensions of the contents inside of the tab component remain consistent when measured.
Why do fonts in my graph component make a test unstable? Certain charting libraries like Highcharts measure the available space to determine where elements should be laid out. Loading a custom font can make the test unstable. Fonts can load before, during, or after the component, and different fonts have different rendered dimensions. The solution we recommend is to use a `` in your [`.storybook/preview-head.html`](https://storybook.js.org/docs/configure/story-rendering#adding-to-head) to preload the font before the story renders. This ensures that the dimensions and position of the fonts inside of the graph component remain consistent.
--- ## Viewports URL: https://chromatic.com/docs/viewports > Configure Chromatic to test responsive UIs at various viewports and breakpoints. # Viewports for responsive UIs with breakpoints UI components respond and adapt to different device widths. Chromatic streamlines visual testing, enabling you to capture each test at multiple viewport sizes. Viewports is compatible with Storybook, Playwright, and Cypress integrations. ## Storybook For Storybook, viewports are configured using the [Modes API](/docs/modes), which allows you to set global configurations that affect how a component renders (e.g., viewport size, theme & locale). Chromatic also integrates with Storybook's viewport feature. You can specify viewports by the names configured in Storybook's `.storybook/preview.js|ts` file. And if a story has a `defaultViewport` set, Chromatic will automatically use that to capture the snapshot. For detailed usage instructions, refer to the [**configure viewports for stories**](/docs/modes/viewports) page. ![Chromatic modes with the viewport feature](../../images/addon-viewports.png) ## Playwright Chromatic will capture the DOM and take snapshots at the viewport size in which a test is configured to run. [Viewports in Playwright](https://playwright.dev/docs/emulation#viewport) can be configured globally in your main Playwright configuration file as follows: ```ts title="playwright.config.js|ts" import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 720 }, }, }, { name: 'Mobile', use: { ...devices['Desktop Chrome'], viewport: { width: 500, height: 600 }, }, }, ], }); ``` Or at the test level: ```ts title="Viewports.spec.js|ts" test.describe('Page display on medium size screen', () => { // 👇 Overrides the option in the test. test.use({ viewport: { width: 1600, height: 1200 }, }); test('Does not display sidebar', async ({ page }) => { await page.goto('/'); }); }); ``` ## Cypress Chromatic will capture the DOM and take snapshots at the viewport size in which a test is configured to run. [Viewports in Cypress](https://docs.cypress.io/api/commands/viewport) can be configured [globally in your main Cypress configuration file](https://docs.cypress.io/api/commands/viewport#Default-sizing) as follows: ```ts title="cypress.config.js|ts" import { defineConfig } from 'cypress'; export default defineConfig({ viewportWidth: 1000, viewportHeight: 660, }); ``` Or using [configuration at the test level](https://docs.cypress.io/api/commands/viewport#Set-viewport-in-the-test-configuration): ```ts title="cypress/e2e/Viewports.cy.js|ts" describe( 'Page display on medium size screen', { // 👇 Overrides the option in the test. viewportHeight: 1000, viewportWidth: 400, }, () => { it('Does not display sidebar', () => { cy.visit('/'); }); } ); ```
ℹ️ Currently, setting the viewport using `cy.viewport()` is not supported.
--- ## Introduction to TurboSnap URL: https://chromatic.com/docs/turbosnap > Speed up tests by detecting file changes with Git # Introduction to TurboSnap TurboSnap is an advanced Chromatic feature that speeds up [UI Tests](/docs#test-how-uis-look--function). It analyzes your project's Git history and builder's [dependency graph](https://webpack.js.org/concepts/dependency-graph/) to identify which components and their dependencies have changed. It intelligently captures snapshots only of the stories associated with those changes. For the rest, it copies over the snapshots from baselines that didn't change. ## How it works 1. Chromatic determines the [ancestor builds](/docs/branching-and-baselines#find-the-ancestor-builds) associated with your commit. 2. TurboSnap considers the git changes between the current commit and the ancestor build's commit. If more than one ancestor build is found, TurboSnap considers the union of the git changes. 3. Using the dependency graph generated by Webpack or Vite, TurboSnap identifies the individual story files affected by those git-identified changes. 4. Chromatic utilizes this information to selectively capture a new snapshot of the stories defined in those specific story files. It also snapshots any tests that were denied on the ancestor build. Chromatic will _not_ capture a new snapshot for stories that do not have associated code changes. This results in faster test runs and reduces the number of billed snapshots. The copied or bypassed snapshots are referred to as **turbosnaps**. If you denied any [UI Tests](/docs/quickstart#4-review-changes) on the ancestor build, Chromatic will always recapture those stories even if TurboSnap would otherwise skip them. This is particularly useful for diagnosing [unstable tests](/docs/unstable-tests#improve-test-stability). ### Full rebuilds Certain code changes have the potential to impact all stories. To avoid false positives, we re-capture everything in the following situations: - Changes to dependency versions in `package.json`, if no valid lockfile is available - Changes to your Storybook's configuration - Changes in files that are imported by your [`preview.js`](https://storybook.js.org/docs/configure#configure-story-rendering) (as this could affect any story) - Changes in your static folder (if specified using `--static-dir` / `-s`) - Changes to files specified by the `--externals` option (see below) - Re-run of the same build (commit and branch match the ancestor build) using the `--force-rebuild` with the CLI - [Infrastructure upgrades](/docs/infrastructure-upgrades) - [UI Test in a new browser](/docs/browsers)
How can I reduce the frequency of full rebuilds? 1. Check your `preview.js` file: you may have a global decorator configured that's [imported from an index or barrel file](/docs/turbosnap/troubleshooting#why-are-changes-not-being-detected-correctly), which itself imports a bunch of other files. This can lead to _all_ stories depending on a large swath of seemingly unrelated files.

Whenever a global Storybook file changes, we run a complete build because global Storybook files can change how _any_ story renders. If any of those files or their imports change, we will trace them as dependent changes. If the file changes are frequent, you can utilize `--untraced` on these files to help ensure you're not triggering a complete rebuild each time the file changes.
Learn more about [how to avoid re-testing for certain files](/docs/turbosnap/setup#avoid-re-testing-dependent-stories-when-certain-files-changed).
2. Make sure you have a valid lockfile(s) available and that it's in sync with your `package.json`. If a lockfile is missing or out ouf sync with `package.json`, we'll have to re-test all stories when there are any changes to either file.
Learn more about how to [avoid re-testing on changes to package control files](/docs/turbosnap/setup#avoid-re-testing-on-changes-to-package-control-files).
### Merge commits Ordinarily, TurboSnap uses git to find all files that have changed since the [ancestor build](/docs/branching-and-baselines#find-the-ancestor-builds) to determine which stories to snapshot. The changed file behavior is more complex with merge commits because there are two “ancestor builds”. When you have a merge commit, we look at both ancestors tied to a build and pull any changes from either build. Chromatic considers **any file that has changed since either ancestor's commit** to decide if a story's snapshot needs to be recaptured. In other words, we take the _union_ of the git changes and not an intersection of them. The reason for this behavior relates to what Chromatic does when it chooses not to recapture the snapshot for a story. In such case, it “copies” the snapshot for the story from one of the ancestor builds, knowing (due to the git check) that the story cannot have changed in the meantime. For merge commits, TurboSnap can't know in advance which side of the merge a snapshot should be copied from, because that requires running the [full baseline selection process](/docs/branching-and-baselines#calculate-a-snapshot-baseline-from-the-ancestor-builds). Since baseline selection happens after TurboSnap, we need to be conservative and allow for changes on either branch. Otherwise, we may miss snapshot comparisons by copying from the wrong branch.
Why is my rebase/squash/merge commit triggering a full rebuild or listing changes from previous commits? When there is more than one ancestor, TurboSnap needs to consider the possible changes triggered by either ancestor. This can lead to situations where we need to retest everything or more stories than you were expecting, even if your branch itself is simple. Let's take a look at an example to understand how a merge impacts the changes TurboSnap considers. ![Git graph where a change to preview.js on feature-2 triggers a full rebuild on F2.1, propagating rebuild tags through merges into main (M.2) and feature-1 (F1.2).](../../images/diagrams/turbosnap-full-rebuild-merge.svg) Suppose an update was made to `preview.js` on the `feature-2` branch, causing TurboSnap to trigger a full rebuild on build `F2.1`. This is expected, since changes to `preview.js` require us to retest everything. The changes from `feature-2` are then merged into the `main` branch in build `M.2`, which now has ancestors from both sides of the merge - `M.1` from `main` and `F2.1` from `feature-2`. The diff between both these ancestors and `M.2` contains the changes to `preview.js`, triggering a full rebuild. Let's say we want to update `feature-1` to merge in the changes that were made on `feature-2`. When we merge the changes from `main` into `feature-1` on build `F1.2`, we trigger another full rebuild. Similarly to the previous build, the diff between `F1.2` and the ancestor builds (`F1.1` and `M.2`) includes the change to `preview.js` that was introduced on `feature-2` and merged in build `M.2`. Since we don't know if the baseline that gets calculated will be taken from `F1.1` or `M.2`, we take from both sides which can lead to merges involving more updates than expected. For example, if it's taken from `F1.1`, the baseline won't have the changes from `preview.js`, and so could be affected. For this reason, we retest all stories in the union of git changes from both sides of a merge, and will do a full rebuild if the change triggers one. This same logic applies even if the change doesn't trigger a rebuild. If we were to imagine the change in `F2.1` was a change to `MyComponent.jsx`, we can assume that `MyComponent.stories.jsx` would be tested in builds `F2.1`, `M.2`, and `F1.2`. While this is less impactful, it's worth noting since this can make a noticeable difference if a lot of files were changed.
### Missing commits (rebasing) Under the hood, TurboSnap works by calculating the difference between the current commit and its ancestors. We get the ancestor builds for the current Chromatic build using the git history related to a current commit `SHA1`. We calculate the ancestor builds by utilizing various `git rev-list` commands to determine the ancestor builds related to your current commit. There are certain cases (i.e., rebasing, squashing, force pushing) where the commit linked to the previous build for the branch no longer exists in the repository, which prevents TurboSnap from doing this computation accurately. In these cases, TurboSnap will traverse your repository's git history until a "replacement build" is found with valid commit history. Once found, it uses the replacement build to approximate the difference between the two commits alongside any UI changes. This can lead to a story being re-tested if one of the following requirements is met: - Git indicates code changes detected between the current and replacement commit - Chromatic detects visual changes identified between the ancestor build and the replacement commit's build
Why do I end up with more than one ancestor build when I rebase? We treat rebases like merges in this regard, since we want to bring baselines across from both sides of the "merge". This way, we don't lose baselines that you've accepted. Let's take a look at an example to illustrate the logic behind why Chromatic takes an ancestor from each side of your rebase. ![Git graph where a feature branch with commits F.1 and F.2 is merged into main as M.2, which takes both F.2 and M.1 as ancestor builds.](../../images/diagrams/turbosnap-rebase-ancestor.svg) Suppose you were to make and accept some changes on `Build F.1`. After you're done making changes to your `feature` branch, you rebase the changes onto your `main` branch - essentially, rebasing `Build F.2`'s commit onto `Build M.2`'s commit. When you do this, those changes from `Build F.1` are going to come over, so you likely want to bring the new baselines over, too. Otherwise, you'll need to re-accept the changes every time you rebase a branch. Since we take baselines from ancestor builds, this means that we need to use `Build F.2` as an ancestor for `Build M.2` in order to track the baseline correctly. Since most folks want to preserve their branch's baseline history, we do this as the default and have a flag available for anyone who wants to ignore the baselines for the last build on a branch (`ignoreLastBuildOnBranch`). Usually, you would only use this option in rare cases where you were doing the rebase in order to _delete_ the baseline changes for the branch.
How does Chromatic determine the ancestor builds for squash/rebase when commits are no longer in the repo? We use information about the branch from git, along with the last Chromatic build on the rebased/squashed branch, to determine the ancestor build for the missing commit. - When you are rebasing a branch off your `main` branch, we can find the last build that ran on the branch before the rebase and use that as an ancestor build. - When you're squash/rebase merging, we can query GitHub, GitLab, or Bitbucket to find out the branch that was just merged, and again use the last build on the branch as an ancestor build.
How does TurboSnap operate when a commit tied to an ancestor build is no longer in the repo? TurboSnap traverses your git history in order to identify what files have been changed between commits. When you squash/rebase, TurboSnap is prevented from accurately computing the difference between the current commit and its ancestor builds. This is because the last Chromatic build on your rebased/squashed branch will not have a matching commit in your git history. The previous commit is removed from your git history, leading TurboSnap to being unable to find the commit. TurboSnap will search for a "replacement build" on the branch with a valid commit in the repo, and use the replacement to approximate the difference between the two commits alongside any visual changes. Let's take a look at an example to understand the ancestor selection logic TurboSnap uses when the commit tied to the previous build on the branch is no longer in your git history. ![Git graph illustrating TurboSnap's replacement-build behavior: feature-1 with rebased commits F1.1 and F1.2 takes M.1 and M.2 as TurboSnap ancestors on F1.3 after merging from main.](../../images/diagrams/turbosnap-replacement-build.svg) In this example, let's say you're working on `feature-1`. There are changes from `feature-2` that were merged into `main`, so you rebase those changes onto your `feature-1` branch on `Build F1.3`. TurboSnap is attempting to find an ancestor build for each side of your rebase on `Build F1.3`. For the `main` branch, TurboSnap is able to find the commit belonging to `Build M.2` since it is the commit rebased onto `Build F1.3`. Things get a little more complicated when TurboSnap tries to locate the commit for the last build on `feature` branch (`Build F1.2`) - the commit no longer exists in your git history. TurboSnap then traverses your git history until a replacement build is found with a valid commit, which is `Build M.1` in our example.
## Pricing Each snapshot incurs billed snapshots: captured snapshots cost 1 billed snapshot and turbosnaps cost 0.2 billed snapshots. The billed snapshot count is used to calculate your monthly bill. ### How turbosnaps are billed By enabling TurboSnap, Chromatic checks for stories with no associated code changes. Instead of capturing new snapshots, it copies snapshots from existing baselines. This uses fewer infrastructure resources, and we pass these savings on to you by billing each turbosnap at 1/5th the cost of a captured snapshot (`0.2`). The remaining stories on that build are captured as usual and cost 1 billed snapshot each. For example, consider a Storybook with 50 stories. If your code changes impact 10 stories, Chromatic will capture new snapshots for those 10 stories. The cost of that build will be 18 billed snapshots: - 10 captured snapshots (1 x 10 = 10 billed snapshots) - 40 turbosnaps (.2 x 40 = 8 billed snapshots) Check out the [billing docs](/docs/billing#with-turbosnap-enabled) for more details. Note, stories excluded by [`OnlyStoryNames`](/docs/configure#onlystorynames) and [`OnlyStoryFiles`](/docs/configure#onlystoryfiles) are also billed at the turbosnap rate. --- ## TurboSnap best practices URL: https://chromatic.com/docs/turbosnap/best-practices > TurboSnap best practices to optimize your builds for faster testing # TurboSnap best practices Most everyone agrees it's important to test your UI, but it's even more important to make sure you're testing the _right parts_ of your UI at the _right time_. Meet TurboSnap: one of Chromatic's most powerful tools for accelerating UI testing in CI. By using your git history and dependency graph to intelligently detect what's changed in your Storybook project so that Chromatic only tests components affected by your updates, saving time and CI resources while maintaining the value of your tests. Whether you're working in a single-project repository or have a more complex setup (such as a monorepo), following best practices ensures TurboSnap works efficiently to help surface _meaningful_ visual regressions. Here are some essential tips to keep your TurboSnap running fast, reliably, and accurately. ## Confirm TurboSnap is enabled Once you've met the [prerequisites](/docs/turbosnap/setup#prerequisites) and started using TurboSnap, it should show as enabled when you visit the "Manage" page of your project. TurboSnap is enabled through the `onlyChanged` option. You can make sure it's enabled for your builds by verifying it's listed within your Chromatic script: ```json title="package.json" { "scripts": { "chromatic": "chromatic --only-changed" } } ``` If you have a Chromatic configuration file (ex. `chromatic.config.json`), check the file to make sure `onlyChanged` is set to `true`: ```json title="chromatic.config.json" { "$schema": "https://www.chromatic.com/config-file.schema.json", "projectId": "Project:...", "onlyChanged": true } ``` Alternatively, it may be in your CI workflow directly: ```yaml title=".github/workflows/chromatic.yml" jobs: chromatic: steps: # ... other steps - name: Run Chromatic uses: chromaui/action@latest with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} onlyChanged: true # 👈 Required option to enable TurboSnap ``` ## Use accurate directory configuration If your Storybook config or preview files don't live in the default `./.storybook` location, you'll need to explicitly tell Chromatic where to find them. Whether in a single-project repo or a monorepo, ensuring your directory configuration is accurate can prevent TurboSnap from tracing unnecessary files. - `storybookBaseDir` sets the root for dependency tracing (what source files are considered part of the Storybook project) - `storybookConfigDir` tells Chromatic where to find your Storybook configuration (the path where your `main.js|ts` and `preview.js|ts` live) If your directories are not properly configured, changes to unrelated files—like your root `package.json` or backend utilities—can trigger full rebuilds.
✨ Use the TurboSnap Helper to set your config! Run `npx @chromatic-com/turbosnap-helper` from the root of your repo to have the helper provide you with the accurate directories and even automatically update your config file—no more guess work!
## Run with caution when using the `pull_request` event TurboSnap is not compatible with the `pull_request` event trigger. This is because the workflow runs against an ephemeral merge commit, which can result in issues with being able to properly track baselines and find earlier builds. We recommend running Chromatic on `push` events. However, if you wish to trigger Chromatic with TurboSnap on `pull_request` events, we've had success with an alternative strategy outlined below. Create a separate workflow for Chromatic using the following strategy for the checkout step: ```yaml - uses: actions/checkout@v6 with: # 👇 Ensures Chromatic can read your full git history fetch-depth: 0 # 👇 Tells the checkout which commit hash to reference ref: ${{ github.event.pull_request.head.ref }} ``` In your workflow, how much of your git history is downloaded when you check out a repo is determined by `fetch-depth`. By default, it's set to `1`, which fetches only the latest commit. It's best to set this to `0` (or a very high number) to ensure Chromatic has access to your full git history. This is crucial for TurboSnap, as it relies on comparing the current commit to the base branch or previous commits. Without the full history, Chromatic may not be able to determine what's changed and could miscalculate what to test. Setting `ref` is one of the most critical parts to using the `pull_request` event, as the `ref` parameter defines what gets checked out into the workspace, which in turn impacts how Chromatic analyses your build. By setting `ref` to `${{ github.event.pull_request.head.ref }}` ensures that GitHub checks out the correct branch and that Chromatic automatically detects accurate git information. There are edge cases where it may be beneficial to include the git environment variables (`CHROMATIC_BRANCH`, `CHROMATIC_SHA`, `CHROMATIC_SLUG`) in your Chromatic step, for example: - If you're using a monorepo with multiple projects and find that you need tighter control across builds. - If you use `actions/checkout` in detached mode (not recommended), or with custom scripts that mutate the git state, and you want to guarantee that Chromatic receives a stable SHA. Otherwise, if you're using `ref` correctly, Chromatic will pick up the right commit via git and optimize with TurboSnap as expected. If you find yourself needing more fine-tuned control over your git environment variables, set all three of the git environment variables using the following env block strategy: ```yaml - name: Publish to Chromatic uses: chromaui/action@latest with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN}} onlyChanged: true env: # 👇 Set to the PR branch if it's a PR; otherwise, falls back to the pushed branch name CHROMATIC_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }} # 👇 Set to the PR head commit if it's a PR; otherwise to the ref (which typically resolves to the latest commit SHA) CHROMATIC_SHA: ${{ github.event.pull_request.head.sha || github.ref }} # 👇 Makes sure this is always set to the corect owner/repo CHROMATIC_SLUG: ${{ github.repository }} ```
These values match what you'd want TurboSnap and Chromatic to see, but _only if the correct code is checked out_. If you're not using `ref` to ensure the correct commit is checked out, setting your git environment variables _will not help_. Without `ref`, your env variables could describle one commit, while the git workspace contains another, causing TurboSnap to have unpredictable results.
## Avoid excessive dynamic imports Dynamic imports, such as `import()` or `require()` with variables or conditions, within your stories, decorators, preview files, or any files they import, can disrupt the chain TurboSnap uses to identify affected stories. This can lead to missed visual changes or unexpected rebuilds. To maintain full traceability, consider minimizing dynamic imports. If dynamic behavior is necessary, incorporate the logic into story-level decorators or within the component itself, and avoid using dynamic imports in the preview file. When it's possible, convert dynamic imports to static imports: - ❌ `const ThemeProvider = require('../themes/default/ThemeProvider');` - ✅ `import { ThemeProvider } from '../themes/default/ThemeProvider';` ## Mind your package control files TurboSnap relies on lockfiles to get actual version numbers rather than semver ranges to attempt to determine an exact set of changed dependencies for more accurate tracing. If your lockfile is missing or out of sync with `package.json`, this prevents TurboSnap from being able to compute the changes and results in a full rebuild. Package file tips: - Make sure you have a valid lockfile checked in and that it's not out of sync with your `package.json` - Avoid unnecessary version bumps in unrelated dependencies - Help reduce noise in lockfile changes by using exact versions ## Optimize shared configs and theming Your Storybook preview file (`.storybook/preview.js|ts`), where you define global decorators, parameters, and theming, is crucial for TurboSnap rebuilds. Any change to this file or its imports can affect the rendering of stories in your project, even if only a single component is impacted. This means changes to theme object definitions, global decorators, context providers, mock setups, and global parameters will trigger a full rebuild. While you **can’t avoid full rebuilds** when changing anything imported by `.storybook/preview.js|ts` (or changing the file itself), here are some best practices to reduce the impact of the preview file changes: - Minimize imports in your preview file. Avoid importing large libraries or full theme objects, and move large objects (like your full theme config) to separate files that don't change often. - Use stable, versioned theme objects. If your project has a design system or uses a component library with theming, import the theme object from a package or file that rarely changes. Importing the theme from a package means changes to the theme require a package release, encouraging discipline and reducing change noise. - Split decorators and providers by scope. If all stories don't need the same decorators, consider moving the logic into story-level decorators when feasible. ## Catch changes that don't pass through your bundler with `externals` TurboSnap depends on the files processed by your bundler. Therefore, it's crucial that files not processed by your bundler also trigger tests. This helps prevent accidental regressions that could have been caught during testing. The `externals` feature was designed specifically for this purpose. It directs TurboSnap to monitor specified files or globs patterns for changes, triggering a rebuild whenever they are modified. Follow these tips to help keep your testing meaningful: - Be selective when using wild glob patterns unless you've audited all files that match the pattern. Having too many files listed in `externals` can lead to noisy or frequent rebuilds. - Make sure the paths you specify are relative to your repository root (which may not be your project root). - Ensure the files are tracked in version control. If the file is ignored by `.gitignore`, it won't trigger a rebuild even if it's listed in `externals`. ## Trace what matters, untrace what doesn't, and scope when possible TurboSnap works by tracing all files that a story depends on, starting from that story's code and walking the import graph. You may find yourself in a situation where some files or folders are frequently updated, causing widespread rebuilds when changed, and not truly impactful to story behavior In these cases, you can tell TurboSnap to exclude paths from being traced using the `untraced` option. You can safely untrace files that: - Don't affect the rendering or logic of your components or stories. - Are never imported (directly or indirectly) by your stories. - Exist solely for documentation, CI scripts, metadata, or non-runtime assets. That said, in some scenarios untracing is dangerous as it compromises your test coverage. You should avoid adding any files to `untraced` that: - Are imported by story files, component files, or decorators. - Contain theme definitions, configuration, or wrapper logic. - Affect how components are rendered—whether directly or indirectly. If a file or folder is frequently updated and has a broad but legitimate impact on your stories, consider scoping it into a dedicated package or a separate Storybook project to reduce its impact radius. This allows TurboSnap to trace only stories affected by the package, which can be tested through Storybook and Chromatic in isolation. Here are few basic guidelines to help you decide when to split: - Theme file changing daily? Move it to it's own package and project. - Global wrapper being used across stories? Extract and import only where needed. - Have a provider that affects all stories? Modularize and apply via story-level decorators if possible. - Preview file importing volatile code? Move it out of preview and apply it in stories or wrappers to keep preview stable. ## Use the `preview-stats.json` to analyze a file's blast radius If you're unsure why a change to a file caused a full rebuild, analyzing your `preview-stats.json` file can help you trace the changes in your dependency graph. This gives you a better visualization of the rebuild scope and helps you determine which stories are affected by your changes. The file is generated locally during Storybook builds, so you can easily inspect it to debug before sending anything through CI. Chromatic provides a [trace utility](/docs/turbosnap/troubleshooting#why-are-no-changes-being-detected) that reads your `preview-stats.json` file to trace changed file paths to their dependent story files. This can help you determine things like: - Whether a small change to a theme file will trigger rebuilds in all stories. - Whether changes to another package will impact your stories. - The actual dependency footprint of a given file. ## How to know what to expect from performance With TurboSnap configured appropriately, you should expect around 80-90% of your Chromatic builds to skip a full rebuild and test only what's changed. For large projects, this often translates to hundreds of files skipped per build, saving both time and snapshots. If you're still seeing 50% or more of your builds trigger a full rebuild of all stories, it's a sign that your configuration may need adjustments or you have shared code that's being modified too often. | Optimization level | Example | Expected full rebuilds (%) | | -------------------- | ------------------------------------------------------------------------------------------------------ | -------------------------- | | No optimization | Default config, shared preview file, barrel files, dynamic imports | **~60-100%** | | Partial optimization | `baseDir` set but noisy preview file | **~30-60%** | | Well-optimized | Scoped preview file, proper externals & modular stories | **5-25%** | | Gold standard | Monorepo with isolated apps, per-package Storybook projects, clean preview files & optimized externals | **1-5%** | ## Conclusion TurboSnap can significantly reduce build times and snapshot volumes, but small configuration choices can greatly impact its performance. It's an advanced feature that requires proper setup and configuration. The best practices outlined above will help you achieve consistent and fast builds with minimal overhead and without sacrificing test coverage. In summary, limit changes to only necessary updates. Modularize global code, declare externals for non-bundled assets, and audit your `preview-stats.json` file using the [trace utility](/docs/turbosnap/troubleshooting#why-are-no-changes-being-detected). --- ## TurboSnap dependency tracing URL: https://chromatic.com/docs/turbosnap/dependency-tracing > Speed up tests by detecting file changes with Git # TurboSnap dependency tracing TurboSnap examines your project's Git history and Webpack's dependency graph to determine which stories might be affected by changes. If you're new to these concepts, then this guide is for you. It'll help you better understand dependency tracing, enabling you to use TurboSnap more effectively. You'll also learn how to trace dependencies in projects using Vite. ## Tracing dependencies with Webpack To start, let's visit [Webpack's dependency graph concept](https://webpack.js.org/concepts/dependency-graph/): > ❝ Any time one file depends on another, webpack treats this as a dependency. This allows webpack to take non-code assets, such as images or web fonts, and also provide them as dependencies for your application. > > When webpack processes your application, it starts from a list of modules defined on the command line or in its configuration file. Starting from these [entry points](https://webpack.js.org/concepts/entry-points/), webpack recursively builds a dependency graph that includes every module your application needs, then bundles all of those modules into a small number of bundles - often, only one - to be loaded by the browser. ❞ TurboSnap leverages [Webpack's Stats Data API](https://webpack.js.org/api/stats) to generate a JSON file with detailed statistics about a project's modules. These statistics allow TurboSnap to analyze a project's dependency graph by providing information on asset objects, related chunks (or grouped modules), and module dependencies linked to the assets. If Chromatic builds your Storybook, it will automatically generate the stats file. But if you provide Chromatic with a prebuilt Storybook, you must add the `--stats-json` (or `--webpack` for Storybook versions 7 and lower) flag to the `build-storybook` command. The stats file can be a bit challenging to read. Therefore, the Chromatic CLI offers a [`trim-stats-file`](/docs/configure/) option to make the file more human-readable. Use it like so: ```shell npx chromatic trim-stats-file ``` Or, if you're using a custom build directory: ```shell npx chromatic trim-stats-file ./path/to/preview-stats.json ``` ### Reading the trimmed stats file After running the `trim-stats-file` command, Chromatic will output a `preview-stats.trimmed.json` file. While this file is more readable, it can still be a bit daunting, so let's break it down with an example. ```json title="preview-stats.trimmed" { "id": "./src/inputs/PinInput/PinInput.tsx", "name": "./src/inputs/PinInput/PinInput.tsx + 4 modules", "modules": [ { "name": "./src/inputs/PinInput/PinInput.tsx" }, { "name": "./src/inputs/PinInput/SeparatedPinInput.tsx" }, { "name": "./src/inputs/PinInput/SinglePinInput.tsx" }, { "name": "./src/inputs/utils.ts" }, { "name": "./src/inputs/PinInput/PinCaret.tsx" } ], "reasons": [ { "moduleName": "./src/data/DataGrid/cells/EditablePinInput.tsx" }, { "moduleName": "./src/inputs/PinInput/index.ts" } ] } ``` In this example, we have an asset with the `chunkName` of `./src/inputs/PinInput/PinInput.tsx + 4 modules`. There's a total of five modules within this chunk: ``` ./src/inputs/PinInput/PinInput.tsx ./src/inputs/PinInput/SeparatedPinInput.tsx ./src/inputs/PinInput/SinglePinInput.tsx ./src/inputs/utils.ts ./src/inputs/PinInput/PinCaret.tsx ``` The `"reasons"` key provides information about the asset's dependency graph. In other words, modules that depend on this asset. Any changes to the asset chunk's modules may have an impact on its dependent modules: ``` ./src/data/DataGrid/cells/EditablePinInput.tsx ./src/inputs/PinInput/index.ts ``` ### Asset object for a story file The asset object for a story file usually looks a bit different: ```json title="preview-stats.trimmed" { "id": "./src/inputs/PinInput/stories/PinInput.stories.tsx", "name": "./src/inputs/PinInput/stories/PinInput.stories.tsx + 4 modules", "modules": [ { "name": "./src/inputs/PinInput/stories/PinInput.stories.tsx" }, { "name": "./src/inputs/PinInput/stories/PinInputWithNoCopyPaste.storyfile.tsx" }, { "name": "./src/inputs/PinInput/stories/PinInputWithNoCopyPaste.storyfile.tsx?raw" }, { "name": "./src/inputs/PinInput/stories/PinInputWithValidation.storyfile.tsx" }, { "name": "./src/inputs/PinInput/stories/PinInputWithValidation.storyfile.tsx?raw" } ], "reasons": [ { "moduleName": "./src/ lazy ^\\.\\/.*$ include: (?%21.*node_modules)(?:\\/src(?:\\/(?%21\\.)(?:(?:(?%21(?:^%7C\\/)\\.).)*?)\\/%7C\\/%7C$)(?%21\\.)(?=.)[^/]*?\\.stories\\.(js%7Cjsx%7Cts%7Ctsx))$ chunkName: [request] namespace object" } ] } ``` Under "reasons," you'll notice a `moduleName` resembling a path pattern. In the full, untrimmed file, the `issuerPath`'s are `"./storybook-config-entry.js"` and `"./storybook-stories.js"`. That's because the dependency is related to this project's Storybook configuration: ```ts title=".storybook/main.ts" import type { StorybookConfig } from '@storybook/react-webpack5'; const config: StorybookConfig = { stories: [ { directory: '../', files: '**/*.@(mdx|stories.@(js|jsx|ts|tsx))', }, ], // ... }; export default config; ``` ## Tracing dependencies with Vite Vite support for TurboSnap is available out of the box, starting with Storybook 8.0 and later, and does not require any additional configuration. However, if you're using an older version of Storybook, you must install the [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap) plugin to enable TurboSnap support with Vite. Similar to Webpack, when you run the `build-storybook` command, the `preview-stats.json` file is automatically generated. It contains information about each module being built and a mapping between each file and all the imported files. This structure mirrors that used by Wepack but only includes the information TurboSnap needs to perform dependency checks. ## Next: Use the stats file to trace dependencies If you're trying to understand why a story file is being detected as changed, you can search for its file path in the trimmed stats file and trace its dependencies to see which modules were affected. Chromatic has a trace utility to simplify this process. In this next chapter, learn how to use the trace utility to analyze your dependency graph. Identify and remove unnecessary imports, diagnose false positives from build tools, and understand the real propagation of changes for optimized bundles and confident refactoring. Read next chapter --- ### Troubleshooting
How do circular dependencies impact tracing? TurboSnap uses _tree-shaking_ to efficiently identify which files in your project have changed and how those changes propagate through your dependency graph. Tree-shaking may not work as intended when circular dependencies exist because the loop can obscure the true "consequences" of changes. Circular dependencies occur when two or more files depend on each other directly or indirectly, creating a loop. For example: - `Button/index.ts` exports `Button.tsx`. - If `Button.tsx` imports something from `Card/index.ts` (directly or indirectly), and `Card.tsx` imports something from `Button/index.ts`, a Circular Dependency is created. When TurboSnap flags unchanged files as changed, you can use the [`--untraced`](/docs/turbosnap/setup#avoid-re-testing-dependent-stories-when-certain-files-changed) flag to correct its behavior by excluding certain files. However, restructuring your project to avoid circular dependencies may be a better, long-lasting solution.
Why transitive dependencies are not tracked? _Transitive dependencies_ occur when a module indirectly depends on another through other modules. Here, module `C` is a transitive dependency of module `A` because it is imported into module `B`: ``` // Module A import B from './B'; // Module B import C from './C'; ``` Sometimes, Webpack struggles to handle transitive dependencies effectively due to standard configurations, specific project structures, or dependency patterns, making it hard to identify the root cause of dependency tracking issues. #### Using the `sideEffects` Property Set the `sideEffects` property in your `package.json` to prevent accidental removal of dependencies and track side effects in third files. There are two ways to set it: 1. Set `sideEffects` to `true` to track all transitive dependencies: ``` "sideEffects": true ``` 2. Specify `sideEffects` as an _array_ to explicitly list files or patterns: ``` "sideEffects": [ "**/*.css", "**/*.scss", "./src/global.js" ] ``` #### Testing the Configuration To verify this solution, add a specific file with transitive imports to the `sideEffects` array and observe whether it behaves as expected. Adjusting the `sideEffects` property can cause other unexpected consequences, so we recommend reviewing the [Webpack Documentation on Tree-Shaking](https://webpack.js.org/guides/tree-shaking/).
--- ## Optimizing TurboSnap for monorepos URL: https://chromatic.com/docs/turbosnap/monorepo-usage > Tips to optimize your TurboSnap configuration when working with a monorepo # Optimizing TurboSnap for Monorepos For large teams using a monorepo and managing changes across dozens of packages, it's crucial not just to run tests faster but to run the right ones. TurboSnap excels in these scenarios by accelerating test runs, executing UI tests only on what has actually changed. However, poorly structured dependencies can derail your whole setup. Constantly triggering full rebuilds or skipping coverage on affected components makes it impossible to trust the results. In this guide, we’ll break down practical strategies for managing `dependencies`, `devDependencies`, dynamic imports, and `package.json` files in a way that keeps your TurboSnap builds snappy _and_ your coverage meaningful.
✨ **Try TurboSnap Helper!** Run our helper utility to accurately configure TurboSnap, even in monorepos—no more guess work! [Read how you can run the utility and get instant help with your config.](/docs/turbosnap/setup#update-your-configuration-using-turbosnap-helper)
## Prefer importing from built packages over `src` Do you have components that are part of a design system or shared library? If so, import them from the built package (ex. from `node_modules/@your-org/ui`). Avoid importing directly from `src` unless you want the story to retest when the imported file changes. This is because TurboSnap uses git tracking to detect changes. Built packages are usually excluded from git (ex. `dist` is in `.gitignore`), so they won't trigger unnecessary story rebuilds unless the _consumer_ has actually rebuilt and re-imported them. Some teams intentionally import from `src` for faster development. If this is the case, consider the tradeoffs and whether _faster development_ or _fewer unnecessary tests_ are more important to the team. ## Use project-specific lockfiles carefully in monorepos When using tools like Nx or pnpm workspaces with project-specific lockfiles, be aware that changes to a `package.json` file (without a valid lockfile) or modifications to lockfiles can trigger full rebuilds. With Nx, you can mitigate this by using `implicitDependencies` in your Nx project configuration. This allows you to specify which stories are affected when a package file changes, reducing the scope of rebuilds. Although TurboSnap doesn't have native awareness of affected files, you can use `nx affected` (or an equivalent tool) to determine the need to rebuild. This approach leads to more targeted tests and minimizes unnecessary full rebuilds. ## Avoid changes to shared or root-level package files unless necessary Changing dependencies at the root or in shared `package.json` files will often trigger TurboSnap to retest all stories. When possible, limit root-level `package.json` changes to toolchains or devDependencies. Keep shared runtime dependencies in leaf packages (a standalone UI component or package that isn’t imported by any other internal packages) so they're not a dependency for any other package. ## Use `devDependencies` to avoid unnecessary rebuilds TurboSnap uses your bundler's (Webpack, Vite, Rsbuild, etc.) dependency graph to determine which stories are affected by changes. That means changes to runtime dependencies (`dependencies`) can ripple into the UI, while development-only tools (`devDependencies`) often don’t, unless they’re used in your Storybook config or components. We recommend: - Keeping runtime dependencies (like `react`, `styled-components`, `axios`) in `dependencies`. - Using `devDependencies` for build tools (ex. `vite`, `eslint`, `typescript`, Storybook addons). - Avoid using dev-only packages in `preview.ts` or inside components—if they’re needed at runtime, they should be in `dependencies`. ## Group and isolate high-churn utilities If you have shared utility functions that frequently change, you may see more retests than expected. Every change to those utility files will cascade to every component that imports from the package, which can trigger TurboSnap to retest all related stories. Instead, isolate those utilities into a clearly scoped package and avoid re-exporting them through your UI library or core package. Import the utilities in your app code and not your shared design system unless they're directly UI-related. ## Avoid dynamic imports in preview files Dynamic imports can disrupt TurboSnap's static analysis of your dependency graph. Since paths are resolved at runtime, it's best to avoid them in story files unless absolutely necessary. That's because it prevents TurboSnap from accurately tracing changes, and using dynamic imports in your preview file can trigger unexpected rebuilds. To ensure full traceability, always use direct imports in your preview and shared utility files. **Why does this happen?** Dynamic imports break the chain TurboSnap relies on to identify affected stories. This can lead to either under-testing or over-testing. Direct imports ensure that the dependency graph can follow the file, identifying which components are affected. As a result, TurboSnap only retests stories that directly or indirectly depend on the modified file. ## Avoid excessive wrapper indirection You **can’t avoid full rebuilds** when changing anything imported by `preview.ts`, but you reduce how often it happens and improve your team’s visibility by avoiding excessive wrapper indirection. Excessive wrapper indirection may look like `withTheme()` wrapping stories with a provider theme, then being imported by `withGlobalProvider()` which wraps the stories in locale. Flattening this structure so `withTheme()` and `withLocale()` are individually defined makes it easier to see which dependencies are responsible when retesting. It also lets you bypass the preview file for some stories by importing the providers directly in specific stories, which would ensure only those files are tested when changes are made to the providers. When possible, use direct imports and consider moving stable wrappers into `preview.ts`. If you're using frequently changing wrappers or features, try to limit them to individual story files to minimize their impact. ## Document your dependency graph Larger teams working out of a monorepo can benefit from clearly identifying which packages depend on which. Tools like [Nx Graph](https://nx.dev/features/explore-graph) (or similar) can help your team visualize your dependency graph and avoid unintentional coupling between packages. ## Monitor `changedPackageFiles` in Chromatic builds If you notice frequent rebuilds due to changes in package files, keep an eye on Chromatic's CLI output for `changedPackageFiles`. TurboSnap will list all package files that triggered a rebuild. Investigate whether the changes are necessary, if there are opportunities to better isolate your packages, or if you should consider using `--untraced` to ignore the packages. ### Use caution when marking `package.json` files as untraced Monorepos often include multiple `package.json` files, and some may trigger rebuilds unnecessarily. You can use `--untraced` to ignore these files. Use the following guidelines to determine if it's safe to mark a `package.json` file as untraced: ✅ **It’s safe to mark `package.json` as untraced when:** - The package is not consumed by Storybook or preview files - The file does not affect the runtime or build output (ex. purely backend or tooling packages) - You use a shared lockfile (`pnpm-lock.yaml`, `yarn.lock`), ensuring consistent resolution - You rely on `Nx affected` or similar tooling ❌ **Do not untrace if:** - The package includes components, themes, or utilities used in stories - The package influences global configuration or decorators in `preview.js|ts` - Changes to it could result in different builds or runtime behavior ## Conclusion Smart dependency management in a monorepo can drastically improve TurboSnap performance and confidence, making it easier and faster to test changes before pushing them to production. By being intentional with how you structure `dependencies`, avoiding dynamic imports in shared config, and tracing changes clearly, you’ll strike the perfect balance between speed and reliability in your visual testing pipeline. Ready to optimize your builds? Start by auditing your most frequently rebuilt packages! Looking to get more details on dependency tracing with TurboSnap? Head over to our docs to read more about [TurboSnap dependency tracing](/docs/turbosnap/dependency-tracing/). --- ## Diagnose preview rebuilds URL: https://chromatic.com/docs/turbosnap/preview-rebuilds > Unexpected changes in your preview file can result in more rebuilds than anticipated. Learn how to use the `trace` utility to identify the root cause of these preview rebuilds. # Diagnose preview rebuilds Global decorators or parameters can affect every story. Therefore, TurboSnap retests everything whenever any file imported within `.storybook/preview.(js|ts)` changes. This includes anything your preview file imports directly or transitively, such as barrels, shared utilities, and theme objects. Depending on your project's structure, even minor changes can bubble up to your preview file, resulting in more rebuilds than anticipated. Diagnosing these rebuilds, especially when caused by transitive triggers, can be challenging. They are most commonly due to: - **Barrel/index files** imported into your preview file. All other files within the barrel create an indirect link, so any changes within them will trigger a rebuild. - **Global decorators or parameters** (e.g.: theme, i18n, etc.) imported by preview. All files that feed into these global configurations become part of preview's dependency scope. - **Shared package imported from source in preview** (e.g.: `packages/ui/src/**`). When preview imports the source directly, any change in the shared package source automatically propagates through preview to all consumers. If you notice this rebuild behavior, identify which import caused the changes to pinpoint the root cause. ## Steps to identify the root cause Let's walk through the steps to identify the specific file change and its link to your preview file. ### 1. Confirm changes First, confirm which files TurboSnap has detected as changed. Add Chromatic's `--debug` (or `debug: true` in GitHub Actions) option to ensure the CLI prints the files identified as changed. ```bash npx chromatic --project-token= --only-changed --debug ``` If you need to reproduce the list locally, you can run Chromatic on the same commit as CI using `--dry-run`: ```bash npx chromatic --project-token= --only-changed --debug --dry-run ``` In Chromatic's CLI output, all changes will be printed out: ```bash 16:31:15.920 Found 14 changed files: .storybook/preview.tsx chromatic.config.json package-lock.json package.json src/components/Codeblock.module.css ... ``` ### 2. Build the dependency graph locally Whether you opt to manually dive in or use Chromatic's `trace` utility, you'll want to make sure you have built the graph: ```bash npm run build-storybook -- --stats-json ``` To make the graph more human-readable, create a trimmed version: ```bash npx chromatic trim-stats-file ``` ### 3. Check for a direct match Open your preview file and scan its top-level imports. If any file in "Files changed" matches exactly with one of those imports, that's your trigger. Common culprits include: - `./decorators/theme` - `./i18n` - `.msw/init-browser` - Broad `index.(js|ts)` barrels ### 4. Look for the barrel footprint If there's no exact match, check whether preview imports from any barrel files (e.g.: `./decorators/index.js`, `@org/theme/index.ts`). If the "Files changed" list includes any file under the barrel's folder, that change likely bubbled up through the barrel to preview. ### 5. Monorepo `src` imports If you're in a monorepo, check to see whether preview imports package source directly (e.g.: `packages/ui/src/..`). If "Files changed" includes files under that source tree, that's the trigger. ### 6. Confirm the change impacts config Pick your suspicious file from "Files changed" and confirm the changed file is triggering rebuilds using the [`trace` utility](/docs/turbosnap/trace-utility): ```bash npx chromatic trace ./helpers/my-helper.jsx ``` If it is, you should see a notice in the CLI that TurboSnap is disabled due to changes found in your preview file: ```bash ⚠ TurboSnap disabled due to file change Found a Storybook config change in .storybook/preview.tsx A full build is required because this file cannot be linked to any specific stories. ``` You can verify whether the file change is the only trigger for the build. Use `--untraced` to remove tracing for the file and trace any other suspicious files that have changed: ```bash npx chromatic trace ./helpers/my-truncator.jsx --untraced ./helpers/my-helper.jsx ``` ### 7. Narrow to the "first hop" under preview If you're still unsure which file linked to the preview is triggering a change, take the likely import and use `--untraced` to determine if it's pulling in the change when tracing another file. ```bash npx chromatic trace ./src/components/Codeblock.module.css --untraced ./helpers/my-helper.jsx ``` You'll either see the same config change message confirming it's in preview, or a dependency path to each impacted story. If you no longer see the config change message, you've isolated the "first hop" that's pulling in the change. ### 8. Manually trace changed files If you've narrowed down the culprit to a file, but you're not sure how it's linked to preview, start by opening your trimmed preview stats file. Search for the file to locate it's module dependencies, then trace the module dependencies for those files until you reach the preview file. Learn more about [reading the trimmed stats file...](/docs/turbosnap/dependency-tracing/#reading-the-trimmed-stats-file) ## Quick read of large module arrays If the CLI shows a long array of possible preview modules (lots of JS helpers, MSW inits, i18n, etc.), treat the list as a menu of imports under preview. - Use steps 3-5 to find the most plausible match against the “Files changed” list. - Use candidates from steps 3 & 4 to confirm they're truly part of the config closure for this build. - If it's a merge build, remember you're seeing upstream changes, too. Your branch might not have touched them, but they’re still in scope. ## Conclusion By the end of this flow, you should have both the specific file(s) that changed and the specific import under preview that pulled it into the config closure. From there, you can decide whether to keep that import global, split it, or reduce its surface (ex. separate barrels, lighter theme entry, etc.): - If the import does not impact rendering or logic of your components or stories, it's safe to add the file to `--untraced`. - If it impacts rendering, it's best to [consider options](/docs/turbosnap/trace-utility/#suggested-fixes) for restructuring your imports to avoid excessive dependency scoping. --- ## Setup TurboSnap URL: https://chromatic.com/docs/turbosnap/setup > Speed up tests by detecting file changes with Git # Setup TurboSnap Enable TurboSnap by running Chromatic's CLI with the `--only-changed` option. Alternatively, you can use the `onlyChanged` option for the Chromatic [GitHub action](/docs/github-actions#enable-turbosnap). It will build and test stories that may have been affected by the Git changes since the last build. Depending on your project setup, you may need [additional configuration](#configure).
⚠️ When using TurboSnap, your builds may complete in less time using fewer snapshots. However, we don't allow using TurboSnap immediately when starting out with Chromatic since the configuration is more complicated and can lead to difficult to debug scenarios or UI changes being missed. Instead, become familiar with Chromatic's out-of-the-box behavior, and once your project has been running smoothly, consider trying out TurboSnap. TurboSnap is unlocked after ten successful builds on CI.
## Prerequisites - Chromatic CLI [10.0+](https://www.npmjs.com/package/chromatic) - Storybook 6.5+ or Vitest 4+ - Git 2.28.0+ - Webpack or Vite based project (Vite is natively supported with Storybook 8+ and can be used in earlier versions with the [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap)) - Stories correctly [configured](https://storybook.js.org/docs/configure#configure-story-loading) in Storybook's `main.js` - 10 successful builds on CI - For GitHub Actions: run on `push` rather than `pull_request` ([learn more](#github-pull_request-triggers)) - UI Tests should be enabled
ℹ️ Most of this guide is for TurboSnap with Storybook. To set up TurboSnap with Vitest, follow the [dedicated setup guide](/docs/vitest/turbosnap).
## Configure To enable TurboSnap for your project, add the `--only-changed` flag to your `chromatic` script, or add the `onlyChanged: true` option to your GitHub workflow config. ```json title="package.json" { "scripts": { "chromatic": "chromatic --only-changed" } } ``` Or for GitHub Actions: ```yaml title=".github/workflows/chromatic.yml" steps: # ... - name: Run Chromatic uses: chromaui/action@latest with: projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} onlyChanged: true ``` TurboSnap can also be enabled through your `chromatic.config.json` file: ```json title="chromatic.config.json" { "$schema": "https://www.chromatic.com/config-file.schema.json", "projectId": "Project:...", "onlyChanged": true } ``` You may need additional config in the following situations: - You're using `--storybook-build-dir` or `-d` to let Chromatic use a prebuilt Storybook - You are using the `staticDirs` config in your main Storybook configuration - You have other files outside the Webpack dependency tree which affect your stories (e.g., Sass or template files) - You have files that should never trigger a re-test (e.g., in a monorepo) - You want to enable or disable TurboSnap for specific branches Jump to the [recipes](#recipes) section for more information. ### Update your configuration using TurboSnap Helper Having TurboSnap properly configured is essential when trying to optimize build performance. If you're working in a monorepo, or even just a large project with a complex setup, setting up TurboSnap efficiently can feel overwhelming. That's where the TurboSnap helper comes in! We've introduced the `@chromatic-com/turbosnap-helper` CLI utility to simplify the process of managing your configuration. The TurboSnap Helper utility is designed to assist with properly configuring TurboSnap for Chromatic. It's especially helpful in monorepos, where managing directories, static assets, and build optimizations across multiple projects can get tricky. Run the utility from the root of your repo using: ```shell npx @chromatic-com/turbosnap-helper ```
❓ **Why run the utility from the repo root rather than the project root?** TurboSnap Helper supports monorepos. By starting from the root, it can scan across all of your projects, so you're not accidentally missing assets or settings that could cause full rebuilds. If you run the utility from your project root, not a problem. The utility will still work just fine.
#### Helper modes to give you a deeper analysis The utility can run in three modes: - `init` (default) - walks you through creating or updating your TurboSnap config - `analyze` - reviews your story and component files for dynamic imports - `preview` - analyzes your preview file for shared dependencies that could cause full rebuilds Running `init` mode not only prints out configuration options in the CLI, but **automatically adds the correct config options** to your Chromatic config file. We've enhanced the utility to take it beyond directory config help and introduced static asset detection, allowing you to easily add static assets to your `externals` config to ensure you don't miss changes. Don't have an existing Chromatic config file? No worries—the utility will create one for you! To help further optimize your builds, the utility has an `analyze` mode that scans both story and components files to detect and surface dynamic imports. Dynamic imports are a hidden culprit for confusing TurboSnap behavior that can result in missed regressions or unnecessary rebuilds. `analyze` mode helps you track them down early. And last is the `preview` mode, which displays an analysis of your Storybook preview file. Use the analysis to help keep your preview file clean and efficient, as a messy preview file can turn small changes into **full rebuilds**. The analysis provides: - Warnings if your import count is too high (the higher the imports, the more likely you'll see frequent rebuilds) - Lists shared wrappers/themes that could trigger global rebuilds - Prints any dynamic imports inside your preview file ### Verify that TurboSnap is working The best way to see if TurboSnap is working is to inspect your CLI output. There are a couple of messages the CLI outputs of particular relevance: ```shell Traversing dependencies for X files that changed since the last build ```
This message tells us how many git changes Chromatic has detected since the last Chromatic build. Usually, that's just one or two commits' worth of files.
```shell Found Y story files affected by recent changes ```
This message tells you the number of story files that depend on the X changes above. This message also might be replaced by a message telling you that we need to capture all stories ([see below](/docs/turbosnap#full-rebuilds) ).
```shell Tested A stories across B components; capture C snapshots in S seconds. ```
This message tells you how many snapshots we actually took instead of the number of stories we found in your Storybook. Usually, C would be the number of stories in the Y component files above.
Once TurboSnap is activated, all subsequent builds will display an indicator with TurboSnap's status. Find it on the Build page above your tests. ![TurboSnap indicator](../../images/build-turbosnap.png) --- ## Recipes A few common scenarios require additional configuration. ### TurboSnap with prebuilt Storybook using Webpack If you're using `--storybook-build-dir` to provide a prebuilt Storybook, adjust your `build-storybook` script to include the `--stats-json` option (or `--webpack-stats-json` option for projects that haven't migrated to Storybook 8+). If Chromatic builds your Storybook for you, this is unnecessary, and will take care of it. For example: ```json title="package.json" { "scripts": { "build-storybook": "build-storybook --stats-json" } } ``` In Storybook 6.2, `--webpack-stats-json` must be set to the value of `--output-dir` (`storybook-static` by default). In Storybook 6.3+, the value can be omitted as it automatically uses the value of `--output-dir`. Note that `--webpack-stats-json` was not supported before Storybook 6.2 and, therefore, cannot be used with TurboSnap.
How can I pass the `--stats-json` flag when using Storybook with `nx`? When using `nx`, you can't pass CLI flags to your `Storybook: build` script since the `nx` executor won't accept them. You must pass `webpackStatsJson` as a [prop to options](https://nx.dev/nx-api/storybook/executors/build#options) in order to generate the stats file.
How can I fail the CI pipeline if `preview-stats.json` file is missing? When TurboSnap is enabled and the `preview-stats.json` is missing, Chromatic doesn't fail the build. Instead, it issues a warning that TurboSnap has **bailed** and runs an un-optimized build. You can add an explicit verification step to your CI pipeline to fail the entire job if the file is not found and you don't want to run full builds. To achieve this, build Storybook in a previous step and place the script below before the Chromatic command: ```yml title="chromatic-ci.yml" STATS_FILE="storybook-static/preview-stats.json" # Adjust the path if; then echo "✅ Success: $STATS_FILE found. Chromatic can proceed." else echo "❌ Error: $STATS_FILE not found. This will prevent TurboSnap from functioning." echo "Check your build script to ensure the --stats-json (or --webpack-stats-json) flag is correctly configured." exit 1 fi ```
### TurboSnap with prebuilt Storybook using Vite For projects using Storybook versions prior to 8, use the [vite-plugin-turbosnap](https://github.com/IanVS/vite-plugin-turbosnap). The plugin will automatically generate a stats file when you run the `build-storybook` command. Vite is natively supported for Storybook 8 and later. To generate the stats file, use the `--stats-json` flag with the `build-storybook` command. ```json title="package.json" { "scripts": { "build-storybook": "build-storybook --stats-json" } } ```
How to generate the `preview-stats.json` file for Rsbuild? Like Webpack and Vite, Rspack should generate a stats file by default. If it doesn't, try setting the `performance.profile` option to `true`. Another method is to use the `performance.bundleAnalyze` option in your `rsbuild.config.ts` and set `generateStatsFile: true`. ```ts title="rsbuild.config.ts" export default { performance: { bundleAnalyze: { analyzerMode: 'disabled', // Optional: prevents the HTML report from opening generateStatsFile: true, }, }, }; ``` If you are using Rspack CLI, you can use the `--json` flag to output the stats file. ```shell npx rspack --json stats.json ```
How to generate the `preview-stats.json` file for an Angular project? When using Angular with Storybook, directly passing a CLI flag will not generate the `preview-stats.json` file. You must configure the Storybook builder directly within your [`angular.json` file](https://storybook.js.org/docs/get-started/frameworks/angular#how-do-i-configure-angulars-builder-for-storybook) by adding `"webpackStatsJson": true`. ```json title="angular.json" { "projects": { "your-project-name": { "architect": { "build-storybook": { "builder": "@storybook/angular:build-storybook", "options": { "outputDir": "dist/storybook/your-project-name", "configDir": ".storybook", "browserTarget": "your-project-name:build", "compodoc": false, "webpackStatsJson": true } } } } } } ```
### Specify a deviating Storybook base directory If you're using a prebuilt Storybook, and your `build-storybook` script was not executed from the same directory where you're running `chromatic`, you'll have to specify the relative path to the Storybook project root (where you run `build-storybook` from). For example, when your Storybook lives at `./services/webapp` in your Git repository: ```json title="package.json" { "scripts": { // This would be a different package.json than the one with `build-storybook` "chromatic": "chromatic --only-changed --storybook-base-dir services/webapp" } } ``` If you're running `chromatic` from the same subdirectory as `build-storybook`, this should not be necessary, as Chromatic will auto-detect the correct base dir. ### Specify external files to trigger a full re-test when they change TurboSnap relies on Webpack's dependency graph. That means if you're using files processed externally to Webpack, with the output consumed by Webpack, you'll need to trigger a re-test when they change. This includes static assets like fonts, images and CSS files, as well as files that compile to static assets such as Sass, so long as they are not processed through a Webpack loader. For example, if you use an external Sass compiler (not `sass-loader`) to compile `.sass` files to `.css` files (which may then be consumed by Webpack), then a change to a `.sass` file will not match any dependencies, preventing stories from being captured (i.e., snapshotted). To work around this, run Chromatic's CLI with the `--externals` flag to specify one or more globs of "externally processed" files. For example: ```shell chromatic --only-changed --externals "*.sass" --externals "public/**" ```
Globs not working as you expected? Verify your pattern using this [picomatch-playground](https://tools.chromatic.support/globs/).
If you've set up TurboSnap with Chromatic's [GitHub action](/docs/github-actions#enable-turbosnap), you can extend your existing workflow and provide the `externals` option as follows: ```yml title=".github/workflows/chromatic.yml" # Other necessary configuration jobs: chromatic-deployment: steps: # 👇 Adds Chromatic as a step in the workflow - name: Run Chromatic uses: chromaui/action@latest # Options required to the GitHub chromatic action with: # 👇 Chromatic projectToken, refer to the manage page to obtain it. projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} externals: | *.sass public/** ``` #### Add `staticDirs` to external files If you are using the [`staticDirs`](https://storybook.js.org/docs/configure/integration/images-and-assets#serving-static-files-via-storybook-configuration) option in your main Storybook config (introduced in Storybook 6.4), you should flag those as externals as well. Whereas, the deprecated `--static-dir` (`-s`) Storybook CLI flag is auto-detected, the config option in `main.js` is not. ### Avoid re-testing dependent stories when certain files changed You may have certain files in your Webpack dependency graph which are (indirectly) used by a story but which you know are unlikely to cause a meaningful (visual) change. A typical example is a global decorator that imports some utility files. Since a global decorator applies to all stories, changing such a utility file would cause the entire Storybook to be re-tested. You can avoid that problem using the `--untraced` flag: ```shell chromatic --only-changed --untraced ".storybook/decorators/*.js" ``` TurboSnap works by taking a list of changed files in your Git repository and tracing those down to a set of story files. The `--untraced` flag allows you to skip tracing dependencies for certain files. That means any file in the Webpack dependency graph matching `--untraced` will be ignored, and thus stories (indirectly) depending on it will not get marked for re-testing at that time. However, those stories still might get marked as a result of tracing another changed file (via a different dependency chain). > Keep in mind that your tests will be less reliable when using `--untraced` because it may skip stories that actually did have meaningful changes. It's recommended to disable TurboSnap on your main branch (see below) so that you can at least catch such changes. `--untraced` is particularly useful when you're importing "index" files that re-export a bunch of underlying modules. A change to any of these modules would cause any file that imports the index file to be considered "dirty", even if it doesn't actually use the changed module. By using `--untraced` on the index file, all of its re-exported modules are automatically untraced as well, as long as they aren't imported directly. #### Avoid re-testing on changes to package control files When certain files that pertain to `node_modules` (`package.json`, `package-lock.json`, `yarn.lock`) change, TurboSnap attempts to determine an exact set of changed dependencies and trace those dependencies to associated stories. We rely on lock file(s) to get actual version numbers rather than semver ranges. TurboSnap retrieves versions for both the current state of the repository and for each baseline commit. If a lockfile is missing or out of sync with package.json, TurboSnap cannot do this, and we'll have to re-test all stories. Similar to source code changes, the `--untraced` flag can also be used to ignore dependency updates (e.g., `--untraced "services/backend/package.json"`). That way, any dependency updates in that package will not be considered when applying TurboSnap. A typical use case scenario would be to untrace the `services/backend/**/*` directory and ignore _any_ changes, including dependencies. If you need fine-grained control over what is untraced, you can also enable it for a specific lockfile, with the caveat that untracing the root-level lockfile will ignore any dependency changes in _all_ packages that rely on it (i.e., sub-packages that don't have their lockfile). ### Enable or disable for specific branches To enable TurboSnap for specific branches, pass a glob to `--only-changed` (e.g., `chromatic --only-changed "feature/*"`). Use a negating glob (e.g. `chromatic --only-changed "!(main)"`) to enable all but certain branches. See the [globs guide] for details. ### Using TurboSnap in a monorepo TurboSnap will make working in a monorepo more efficient. Because it detects affected stories based on the actual files changed, pushing a commit that touched only backend code will run faster in CI and use fewer snapshots. However, it will still build and publish your Storybook. To avoid that, you can [skip Chromatic entirely](/docs/monorepos#only-run-chromatic-when-changes-occur-in-a-subproject), speeding up your CI pipeline even more. When configuring TurboSnap in a monorepo, it's important to ensure that Chromatic understands where your Storybook project is relative to where you're executing your `chromatic` script. This is particularly important because TurboSnap relies on the correct file path resolution to determine which stories have changed and which stories can be skipped. If you're running a subproject out of a monorepo, chances are that you're executing your script from the root of your repository, in which case there are extra considerations to take when configuring TurboSnap. Chromatic's suggested best practice when executing from your repo root is to set your `--storybook-base-dir` and `--storybook-config-dir` flags. For example, when your Storybook project is located in the `packages/webapp` dir of your repo, you'd update your script to reflect the following: ```json title="package.json" { "scripts": { "build-storybook": "build-storybook", "chromatic": "chromatic --only-changed --storybook-base-dir packages/webapp --storybook-config-dir packages/webapp/.storybook" } } ```
If you're using a prebuilt Storybook as opposed to having Chromatic build your Storybook, ensure that you are passing the `--storybook-build-dir` option in your `chromatic` script and [adjust your `build-storybook` script to include `--stats-json`](/docs/turbosnap/setup#turbosnap-with-prebuilt-storybook-using-webpack).
This would ensure we're checking `packages/webapp` for your Storybook project instead of your root directory, which may end up detecting unexpected package control files for other subprojects. Even though the default config directory is being used, you'll want to ensure to set `--storybook-config-dir` as well since this path is relative to your current working directory. If you're using the `--externals` or `--untraced` flags in a monorepo, you'll want to be mindful that the paths specified are relative to your repository root. For example, let's say you want to ignore some global decorators in your `packages/webapp` project: ```shell chromatic --only-changed --untraced "packages/webapp/.storybook/decorators/*.js --storybook-base-dir packages/webapp --storybook-config-dir packages/webapp/.storybook" ``` Using the path from the project's root (`.storybook/decorators/*.js`) would result in the files still getting traced by TurboSnap. Keep in mind you can use a pattern such as `--untraced "**/.storybook/decorators/*.js"`, this would work with TurboSnap and would result in changes to the directory going untraced.
Learn more about [using --untraced in a monorepo](/docs/monorepos#with-turbosnap).
If your monorepo has stories from multiple subprojects coming together in one Storybook, you may consider [running Chromatic on a subset of your Storybook](/docs/monorepos#run-tests-on-a-subset-of-stories). With TurboSnap enabled, that happens automatically. For more fine-tuned control over the subset of stories that get considered for testing, explore using the `--only-story-files` or `--only-story-names` options.
Learn more about using [onlyStoryFiles](/docs/monorepos#with-onlystoryfiles) and [onlyStoryNames](/docs/monorepos#with-onlystorynames) in your monorepo.
[globs guide]: /docs/globs --- ## Compatibility #### GitHub pull_request triggers GitHub workflows have various "triggers" that a Chromatic action could run on. We recommend sticking to `push` unless you know what you're doing. TurboSnap is _not_ compatible with the `pull_request` trigger or its variations. The reason is that `pull_request` workflows run against an ephemeral merge commit, which doesn't exist in your Git history yet, but _would_ if you were to merge the PR at that point. If your pull requests trigger multiple builds before being merged, Chromatic would not be able to find those earlier builds because your Git history does not actually contain the commit for which you ran a Chromatic build. Our own GitHub Action works around that by using `pull_request.head.sha` as the commit hash for the build, even though it's really running against the merge commit so that we can still track baseline history. However, this discrepancy means TurboSnap would be looking at a different set of changed files than were actually in the recorded commit (which depends on the state of your base branch), yielding unpredictable results. --- ## Mastering Chromatic's trace utility URL: https://chromatic.com/docs/turbosnap/trace-utility > Learn how to use the trace utility to analyze your dependency graph. Identify and remove unnecessary imports, diagnose false positives from build tools, and understand the real propagation of changes for optimized bundles and confident refactoring. # Mastering Chromatic's trace utility Modern applications have complex import relationships. A simple utility function might be used by dozens of components, and a change to that function could potentially affect many stories. When using TurboSnap, you might notice that changing one component triggers snapshots for multiple unrelated stories. This happens when changes propagate through your dependency graph in unexpected ways. Chromatic's `trace` utility helps you visualize these connections and optimize your project structure by mapping these relationships so you can understand the impact of your changes. Let's review an example of how you can use the utility to see the impact of changing a component, such as a button: ```bash npx chromatic trace ./src/stories/Atoms/Button.jsx ``` By default, the CLI will output trimmed results: ```bash ℹ Traced 1 changed file to 5 affected story files: — src/stories/Atoms/Button.js [changed] ∟ src/stories/Atoms/Button.stories.js ∟ [story index] — src/stories/Atoms/Button.js [changed] ∟ src/stories/Atoms/Button.stories.js ∟ src/stories/Atoms/Composed.stories.js ∟ [story index] — src/stories/Atoms/Button.js [changed] ∟ src/stories/Pages/SecondPage.jsx ∟ src/stories/Pages/SecondPage.stories.js ∟ [story index] — src/stories/Atoms/Button.js [changed] ∟ src/stories/Components/UserProfile.js ∟ src/stories/Components/UserProfile.stories.js ∟ [story index] — src/stories/Atoms/Button.js [changed] ∟ src/stories/Components/ProfileSummary.jsx ∟ src/stories/Components/ProfileSummary.stories.jsx ∟ [story index] ``` This output gives us insight into the blast radius of the file, letting you determine which stories are affected by changes to this file. For comparison, you can run the same command for an imported CSS file and see if there's any difference: ```bash npx chromatic trace ./src/stories/Atoms/Button.css ``` Perhaps you'll find that the Button styles have a direct impact on a component that doesn't use the Button but reuses some of the Button's styles. Reusing styles isn't recommended because it creates hidden dependencies. Changing a Button's padding could unexpectedly break a card component that happened to import those same styles. The `trace` utility exposes these architectural anti-patterns, showing you exactly how styling changes ripple through your application. ## Using the `trace` utility ### 1. Configure your Storybook to generate stats JSON Ensure that you're generating a stats JSON file during your Storybook build. You can do this by adding the `--stats-json` flag to your build script in `package.json`: ```bash # Build with preview-stats.json npm run build-storybook -- --stats-json # Optional: create a trimmed version npx chromatic trim-stats-file ``` The `preview-stats.json` file contains your complete dependency graph, while the trimmed version removes noise like `node_modules` dependencies for clearer analysis. The `trace` utility requires a stats file in order to map dependencies. ### 2. Trace one or more files to see affected stories Trace a single file by passing the file path: ```bash npx chromatic trace ./src/utils/dateFormatter.js ``` Pass multiple file paths to trace multiple files at once: ```bash npx chromatic trace ./src/components/Button.js ./src/hooks/useButton.js # Files with different extensions npx chromatic trace ./src/utils/helpers.js ./src/utils/constants.ts ./src/styles/main.css ``` ### 3. Adjust verbosity of the output using the `--mode/-m` flag Use `expanded` to see underlying modules: ```bash npx chromatic trace -m expanded src/components/Button/Button.jsx ``` Results: ```bash — src/components/Button/Button.js [changed] ∟ src/components/Button/Button.stories.js ∟ [story index] ∟ src/components/Header/Header.js ∟ src/components/Header/Header.stories.js ∟ [story index] ``` Using `compact` will show only the list of dependent story files, omitting their connections: ```bash # See only affected story files npx chromatic trace -m compact src/components/Button/Button.jsx ``` Results: ```bash ℹ Traced 1 changed file to 3 affected story files: src/components/Button/Button.stories.js src/components/Header/Header.stories.js src/components/App/App.stories.js ``` ## Use globbing features [Basic globbing](https://github.com/micromatch/picomatch?tab=readme-ov-file#basic-globbing) is supported when using the `trace` utility. You can use a `*` character to trace an entire directory: ```bash # ✅ WORKS npx chromatic trace src/components/Button/* npx chromatic trace src/components/* # ❌ WON'T WORK npx chromatic trace src/components/Button/ npx chromatic trace src/components/ ``` Use basic characters (`*`, `**`, `[]`, `?`) to simplify your file path: ```bash npx chromatic trace **/[abc]tomic/*/Accordion.ts? # trace output match Traced files... [ 'src/components/atomic/accordion/Accordion.tsx' ] ``` ## Additional configuration options ### Custom stats file location By default, the `trace` utility expects your stats file to be in the `storybook-static` directory as either `preview-stats.json` or `preview-stats.trimmed.json`. If you're using a custom build directory, your stats file is output to a different directory than your build directory. You can also use the `-s/--stats-file` flag to specify the custom stats file name: ```bash # Use a custom stats file name npx chromatic trace -s storybook-static/custom-stats.json src/components/Button/Button.jsx # Stats file in different directory npx chromatic trace -s build-artifacts/preview-stats.json src/components/Modal/Modal.jsx # With multiple files npx chromatic trace -s my-stats.json src/components/Button/Button.jsx src/components/Modal/Modal.jsx ``` ### Specify base directory in a monorepo If you're running the `trace` utility in a monorepo with multiple Storybook projects or your project is nested, you can pass the `-b/--base-dir` option to specify the package directory: ```bash # Storybook nested in packages/storybook directory npx chromatic trace -b packages/storybook src/components/Button/Button.jsx # Multiple packages structure npx chromatic trace -b apps/web src/components/Button/Button.jsx ``` ### Test impact of ignoring specific files You can test the impact of ignoring specific files using the `-u/--untraced` option: ```bash # Ignore a specific utility file npx chromatic trace src/components/Button/Button.jsx -u src/utils/helpers.js # Ignore a CSS file npx chromatic trace src/components/Button/Button.jsx -u src/styles/shared.css # Multiple specific files npx chromatic trace src/components/Button/Button.jsx -u src/utils/helpers.js -u src/styles/theme.css ``` ## Examples ### Barrel files Let's say you're using a similar component structure that relies on barrel (or index) files: ```txt src/ components/ index.js # exports * from './Button', './Modal', etc. Button/ Modal/ ``` You observe that changing one component affects many unrelated stories. For example, you identify that changing the Button affects Modal stories. Use the `trace` utility to test whether the barrel file is responsible for overly-scoping: ```bash # Confirm that Button changes affect Modal npx chromatic trace ./src/components/Button/Button.jsx # Test if the barrel file is the culprit using --untraced npx chromatic trace ./src/components/Button/Button.jsx --untraced=./src/components/index.js ``` If the second command shows fewer affected stories, you've identified the barrel file as the culprit. Barrel files commonly increase the blast radius of your changes since they may introduce unrelated files to a component's dependency graph. The solution is to skip the barrel file and use direct imports: ```js # Replace import { Button } from './components' # with import Button from './components/Button/Button' ``` Direct imports ensure the dependency graph can follow the file without tracing unrelated modules. This is essential for global configurations (such as your `.storybook/preview` file) and barrel files with wide imports. ### Shared utility dependencies Let's say you'd like to trace a currency utility: ```bash # You updated a formatting function npx chromatic trace ./src/utils/formatCurrency.js ``` A change to this utility function affects multiple components unexpectedly. The output shows it affects: ```txt - ProductPrice.jsx - CartSummary.jsx - InvoiceTable.jsx - AdminDashboard.jsx (unexpected!) ``` It's necessary to review how the currency utility is being imported by the Admin Dashboard component and other components being imported into Admin Dashboard. If you discover a third file cascading the changes to `AdminDashboard.jsx`, you can try testing the impact of ignoring it: ```bash # Trace without the third file to see if they're truly needed npx chromatic trace ./src/utils/formatCurrency.js --untraced=./src/components/ThirdFile.jsx ``` This helps you distinguish between direct dependencies (components that actually use the utility) and transitive dependencies (components that import other components that use the utility). ### Confirm changes with Git Utilize these Git commands along with `trace` to confirm which files have changed and their impact, based on your dependency graph: ```bash # Trace only files changed in the most recent commit npx chromatic trace $(git diff --name-only HEAD~1 HEAD) # Trace files changed between two specific branches npx chromatic trace $(git diff --name-only develop main) # Trace uncommitted changes npx chromatic trace $(git diff --name-only) ``` ## Suggested fixes ### Unused imports in dependency graph **Problem:** Trace utility reveals dependencies that aren't actually used.
**Solution:** Remove the unnecessary imports entirely.
**Why:** Projects accumulate unused imports over time, silently increasing bundle size and dependency complexity, making it difficult to understand how changes propagate. ### Broad import patterns **Problem:** Components import entire libraries or barrel files (ex. `import * from 'src/components'`).
**Solution:** Replace with specific, targeted imports (ex. `import { Button } from 'src/components/Button'`).
**Why:** Broad imports create hidden dependencies — changing one part of a library can unexpectedly affect many unrelated components. ### Monolithic utility files **Problem:** Large files combine multiple responsibilities.
**Solution:** Split into focused, single-responsibility modules.
**Why:** This allows components to import only what they actually need, reducing the impact radius of changes and making dependencies explicit. ### False positive dependencies **Problem:** Trace shows dependencies that shouldn't logically exist.
**Solution:** Tweak project configuration and advance options.
**Why:** Misconfigured optimization settings (like `sideEffects`) prevent accurate dependency analysis. Another thing that can cause issues with the analysis is the use of wildcard exports in barrel files, as they make tree-shaking more challenging. The trace won't identify what specific option must be changed, but can help identify the issue. ### Widespread but necessary dependencies If dependencies are genuinely used across multiple components, document them to build institutional knowledge. This enables your team to evaluate potential ripple effects before making changes. When dependencies are part of a shared library or design system and are tested separately (or can be isolated for better scoping), importing those components from the built package can be beneficial. This approach helps prevent unnecessary changes from being detected, as built packages are generally excluded from git. For more information, see [importing those components from the built package](/docs/turbosnap/monorepo-usage#prefer-importing-from-built-packages-over-src). ## Next: Use the `trace` utility to diagnose preview file rebuilds When unexpected changes impact your preview file, this can lead to more rebuilds than expected. Learn how to pinpoint the root cause of your preview rebuilds using the `trace` utility. Read next chapter --- ## Troubleshooting TurboSnap URL: https://chromatic.com/docs/turbosnap/troubleshooting > Speed up tests by detecting file changes with Git # Troubleshooting your TurboSnap setup ## Why are no changes being detected? If the build output shows no story files being detected by changes, there might be an issue matching the git changes with the files in your Storybook build. Use the `--debug` flag for more information about what Chromatic is doing. Another reason that changes may be missed is if the changed files aren't directly included in the webpack build; use the [`externals` flag](/docs/turbosnap/setup#specify-external-files-to-trigger-a-full-re-test-when-they-change) to tell Chromatic about this. ## Why are changes not being detected correctly? Teams frequently use an index file ([barrel file](https://basarat.gitbook.io/typescript/main-1/barrel)) to simplify imports. This practice allows for easier importing, such as accessing a component from `@component-library` instead of specifying the full path like `@component-library/elements/table`. However, the usage of barrels impacts TurboSnap's ability to detect changes. When encountering a barrel file, TurboSnap relies on Webpack's tree-shaking abilities to accurately identify changes. Otherwise, a modification in `@component-library/elements/table` would prompt changes in all files importing from the barrel file, meaning all files that import from `@component-library`. Therefore, if you use barrel files, you'll want to ensure that Webpack tree-shaking (dead-code-elimination) is working properly. Here are some tips: 1. Use ES2015 module syntax (i.e., import and export) 2. Build using `production` mode (`NODE_ENV`) to enable [various optimizations](https://webpack.js.org/configuration/mode/#usage) including minification and tree-shaking. 3. Make sure that compilers do not automatically convert your ES2015 module syntax into CommonJS modules. This is the default behavior of the popular Babel preset `@babel/preset-env`. See the [documentation](https://babeljs.io/docs/babel-preset-env#modules) for more details. ## Why is TurboSnap testing more stories than the ones I changed? TurboSnap identifies **updated files** to test their stories. For instance, if you modify a single story in the `Button.stories.js` file, TurboSnap will test all stories in that file because it cannot pinpoint changes to an individual story. You can review how many stories were tested in the TurboSnap tooltip. ## Why are full rebuilds required? Full rebuilds can be required for various reasons (see the list in [how it works](/docs/turbosnap#how-it-works)). Another scenario where a full rebuild will also be required is due to a change to a `package.json` or lock file for a subproject that doesn't affect the Storybook (we need to be very conservative as we cannot tell if a change to a lock file could affect `node_modules` imported by Storybook). ## Why is my build failing with an Out of memory error? An `Out of Memory` error typically happens when the Node.js process runs out of allocated memory, especially if your project has a large dependency tree or TurboSnap is enabled. Here are a few steps that can help resolve this issue: - **Increase Node.js memory allocation:** Try rerunning Chromatic’s CLI with a higher memory limit by setting `NODE_OPTIONS=--max_old_space_size=4096` (4GB) or higher. This provides more memory to Node.js, which can help with larger builds. - **Adjust CI runner memory settings:** Make sure your CI runner has sufficient memory. Chromatic recommends a minimum of 4GB, and if needed, up to 8GB. Some CI providers may require specific configurations to allocate more memory to certain steps or to the entire runner environment. - **Force full rebuilds on lock file changes:** Set the `MAX_LOCK_FILE_SIZE` environment variable to 1 to ensure a full rebuild occurs whenever the lock file changes. This can help prevent OOM errors. Builds are split between your environment and Chromatic’s. Processes that require access to your code (like dependency tracing) run in your environment, while tasks like the Capture process run on Chromatic’s end. ## Why do merge commits test more changes than I expect? Ordinarily, TurboSnap uses git to find all files that have changed since the [ancestor build](/docs/branching-and-baselines#find-the-ancestor-builds) to determine which components/stories to snapshot. The changed file behavior is more complex with merge commits because there are two "ancestor builds". When you have a merge commit, Chromatic considers **any file that has changed since either ancestor's commit** to decide if a story needs to be re-snapshotted. In other words, the union of the git changes. The reason for this behavior relates to what Chromatic does when it chooses not to re-snapshot a story. In such case, it "copies" the snapshot for the story from the ancestor build, knowing (due to the git check) that the story cannot have changed in the meantime. In the case of merge commits, Chromatic does not know ahead of time which side of the merge the snapshot might be copied from because that involves running the [complete baseline selection](/docs/branching-and-baselines#calculate-a-snapshot-baseline-from-the-ancestor-builds) process, so it needs to be conservative and allow for changes on either branch. ## Does TurboSnap work with squash/rebase merge? TurboSnap is compatible with squash and merge rebasing as of version 6.6+. Please update your package to get support. ## Why didn't TurboSnap run on my build? The usage report shows whether TurboSnap actually ran, and if it didn’t, why. | Bail reason column | What happened | | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | One of the [reasons below](#what-do-the-turbosnap-bail-reasons-in-my-usage-report-mean) | TurboSnap ran, but couldn't confirm anything was unchanged, so every test in the build was captured. | | `unavailable` | TurboSnap isn't available for your account yet. It becomes available after 10 builds from CI. | | Empty | Either TurboSnap ran and copied what it could, or it wasn't enabled for that build. | An empty column doesn't distinguish those last two. To tell them apart, open the build in Chromatic and check the TurboSnap status in the test summary. Its tooltip says whether TurboSnap applied, bailed, or was disabled for that build. ## What do the TurboSnap bail reasons in my usage report mean? Your [usage data (CSV report)](/docs/billing/usage-reports#export-usage-data-as-a-csv) can help you identify opportunities to improve your TurboSnap configuration. The **TurboSnap Bail Reason** column tells you what caused TurboSnap to bail and trigger a full rebuild. The **TurboSnap effectiveness** chart in the [Trends dashboard](/docs/usage-trends) segments bails by these same reasons. Most reasons point to something in your setup you can change. `unavailable` and `missingStatsFile` are the exceptions, explained in their entries below. Here's what each reason means: ### `changedExternalFiles` TurboSnap bailed due to matching `externals`. You may need to check your glob pattern or revisit the paths you're including with `externals` if these changes are unexpected. ### `changedPackageFiles` TurboSnap bailed due to one or more package file changes (`package.json`, `package-lock.json`, `yarn.lock`). This bail should be expected from time to time as dependencies are updated, but can become more of an issue if you run out of a monorepo or have dependencies in subdirectories. If you find this happening frequently, consider using `untraced` to [avoid re-testing on changes to your package control files](/docs/turbosnap/setup#avoid-re-testing-on-changes-to-package-control-files). ### `changedStaticFiles` TurboSnap bailed due to matching static file (`--static-dir`). This message should only present to customers using TurboSnap with older Storybook versions, since `--static-dir` was deprecated in Storybook 8. Customers on Storybook 8+ need to add their static dirs using `externals`. This error may indicate that you need to check the value you've configured for `--static-dir`. ### `changedStorybookFiles` TurboSnap bailed due to a change in Storybook config (`.storybook` dir files, such as `main.js|ts` and `preview.js|ts`). You may have made changes to one of the files within your Storybook configuration, or to a dependency that is being imported by your configuration. It's important to keep in mind that second part, since it's common practice to import decorators into the `preview.js` config. Additionally, if you're utilizing barrel/index files (whether directly importing in your `preview.js` or in a file that `preview.js` is dependent on), it's likely to cause more bails since [a change to any of these modules would cause any file that imports the index file to be considered "dirty"](/docs/turbosnap/setup#avoid-re-testing-dependent-stories-when-certain-files-changed), even if it's not using the changed module. ### `invalidChangedFiles` TurboSnap bailed due to missing git history. TurboSnap is having issues retrieving your git history. This commonly happens when a repo is shallow cloning, which can be fixed by setting your fetch depth to `0` in your `.yml` workflow file. Another common cause of this issue is when customers use GitHub Actions with the `pull_request` event. If this is the case, you'll want to make sure you [set the correct `ref` in your checkout step](/docs/github-actions#recommended-configuration-for-build-events). ### `missingStatsFile` TurboSnap bailed due to missing stats file. This reason comes from Chromatic CLI versions before 10.6.0. Newer versions fail the build with an error message instead of bailing, so it only appears in usage reports covering builds that ran on an older CLI. The fix is the same either way. Since the stats file is needed for TurboSnap to analyze your dependencies, make sure your Storybook script includes `--stats-json`. ### `noAncestorBuild` TurboSnap bailed due to not finding an ancestor build for the commit. This bail reason is reserved for when no ancestor build is found at all for a commit. This can be due to rebasing, squash-merging, force-pushing, or running against ephemeral merge commits. If it's happening on builds that don't fit those categories, it may indicate an issue with fetching your git history. ### `rebuild` TurboSnap bailed because the commit is a rerun of a previous build, based on the baseline build having the same commit and branch name. Most reruns are intentional, but if you weren't expecting the ones you're seeing, this may indicate you need to check your CI workflow to see what could be kicking off an additional build for the same commit. ### `unavailable` TurboSnap didn't run because it isn't available for your account yet. TurboSnap becomes available once a project has run at least 10 builds from CI, and it stays available for the whole account from then on. Until that happens, builds that ask for TurboSnap record this reason and capture every test. This isn't a configuration problem, so there's nothing to change in your Chromatic script. Keep running builds in CI. If you've passed 10 builds from CI and TurboSnap still isn't available, check that the project has UI Tests or UI Review enabled, since it won't become available for projects that only publish Storybook. --- ## Composition URL: https://chromatic.com/docs/composition > Learn to combine Storybooks through composition # Storybook Composition Chromatic publishes your Storybook to a secure CDN. That means you can combine published Storybooks with your local Storybook using [Composition](https://storybook.js.org/docs/sharing/storybook-composition).
Chromatic does not snapshot externally composed Storybooks for UI Tests or UI Review.
## Compose published Storybooks Chromatic generates a [permalink](/docs/permalinks) for published Storybooks to use with Composition that includes: - Versioned endpoints, URLs that resolve to different published Storybooks depending on a version=x.y.z query parameter (where x.y.z is the released version of the package). - Support for /stories.json - Support for /metadata.json and the releases field. ### Setup In your local Storybook, add a `refs` key to [`.storybook/main.js|ts`](https://storybook.js.org/docs/configure#configure-story-rendering). Paste the permalink in the `url` field. ```js title="storybook/main.ts" // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { StorybookConfig } from "@storybook/your-framework"; const config: StorybookConfig = { framework: '@storybook/your-framework', stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], refs: { // 👇 Upper-case characters not supported in the refs key "chromatic-published-storybook": { // The title of your Storybook title: "Design System", // The url provided by Chromatic when it was published url: "https://your-published-url.chromatic.com", }, }, }; export default config; ``` When your local Storybook starts, it will auto detect the `refs` and compose your published Storybook. You'll see both sets of stories side-by-side. ![Multiple Storybooks combined through composition](../../images/reference-external-storybooks-composition.png) ### Compose Storybook by branch or commit Depending on your use case, you may want to compose Storybook using a [permalink](/docs/permalinks) to a branch or a commit. #### Branch: `https://--.chromatic.com` If you want your local Storybook to compose the latest Storybook on `main`, use the branch permalink. This is useful for folks who work on multiple Storybooks simultaneously. - Building a component library in React and Vue at the same time - Monorepos with multiple inter-connected Storybook projects #### Commit: `https://--.chromatic.com` If you want your local Storybook to compose a specific version of Storybook, use the commit permalink. This is useful for folks who depend on a fixed version of a component library package. ### Access control Published Storybooks follow the [access rules](/docs/access) of your project. If you have a private project, you'll need sign in to Chromatic (via Storybook's UI) to load the private Storybook. --- ## Package Composition Design system and component library authors can automatically compose their Storybook inside their consumer’s Storybooks. Add a `storybook` property in the `package.json`. Use the [permalink to a commit](#compose-storybook-by-branch-or-commit) in the `url` field. ```json { "storybook": { "url": "https://your-published-url.chromatic.com" } } ``` Once the package is installed and Storybook starts, it scans for external Storybooks referenced by your packages and loads them into the UI. ### Versioning Chromatic supports automatic versioning for the following Git providers. | Git provider | Support | | --------------------------------------------------- | ---------------------------------------- | | GitHub | Public projects only via GitHub Releases | | GitLab | Public and private projects | | Bitbucket | Not supported | | [Unlinked projects](/docs/access#unlinked-projects) | Not supported |
How to manually query for versions? If automatic versioning isn't supported for your Git provider, you can still get version information by manually updating your `package.json` with the permalink of the current published Storybook (e.g. `https://--.chromatic.com`). Use the `/metadata.json` endpoint to get additional information about the deployed Storybook version. It will output a response similar to the example below: ```json { "versions": { "v0.1.1": "https://your-published-url.chromatic.com" } } ```
--- ### Resources - [Storybook composition](https://storybook.js.org/docs/sharing/storybook-composition) - [Package composition with Storybook](https://storybook.js.org/docs/sharing/package-composition) --- ## Embed stories URL: https://chromatic.com/docs/embed > Embed your Storybook on Medium, Notion, and other platforms # Embed stories Embed stories published to Chromatic in Medium articles, Notion pages, and countless other platforms. Before we begin, you'll need to figure out which embed format your platform supports: oEmbed or standard ` ``` ``` ```