コンテンツにスキップ

Chromatic Cheat Sheet

Overview

Chromatic is a cloud-based visual testing and review platform specifically designed for Storybook. It captures snapshots of every story in your Storybook across multiple browsers and viewports, then compares them against approved baselines to detect visual regressions. Chromatic provides a collaborative review workflow for UI changes, similar to code review but for visual changes.

Built by the maintainers of Storybook, Chromatic offers deep integration with the Storybook ecosystem. It supports interaction testing, accessibility checks, component documentation publishing, and integrates with GitHub, GitLab, Bitbucket, and CI/CD pipelines. Chromatic also publishes your Storybook online for team sharing and stakeholder review.

Installation

# Install Chromatic
npm install -D chromatic

# Or with yarn
yarn add -D chromatic

# Or with pnpm
pnpm add -D chromatic

# Verify
npx chromatic --version

Basic Usage

# Run Chromatic (first time - builds and publishes Storybook)
npx chromatic --project-token=YOUR_PROJECT_TOKEN

# Run with auto-accept (for initial baseline)
npx chromatic --project-token=YOUR_PROJECT_TOKEN --auto-accept-changes

# Run against a specific branch
npx chromatic --project-token=YOUR_PROJECT_TOKEN --branch-name=main

# Skip build if no changes
npx chromatic --project-token=YOUR_PROJECT_TOKEN --exit-zero-on-changes

# Use environment variable for token
export CHROMATIC_PROJECT_TOKEN=YOUR_PROJECT_TOKEN
npx chromatic

CLI Options

FlagDescription
--project-tokenProject token for authentication
--auto-accept-changesAuto-accept all visual changes
--exit-zero-on-changesExit 0 even if there are changes
--exit-once-uploadedExit after upload (don’t wait for results)
--skipSkip Chromatic entirely
--only-changedOnly test stories that changed
--only-story-namesFilter stories by name pattern
--only-story-filesFilter by story file paths
--externalsGlob patterns for external files that affect stories
--storybook-build-dirUse pre-built Storybook directory
--build-script-namenpm script to build Storybook
--branch-nameOverride detected branch name
--patch-buildCompare against specific base build
--no-interactiveDisable interactive mode
--debugEnable debug output
--dry-runRun without uploading
# Only test changed stories (TurboSnap)
npx chromatic --only-changed

# Specify external dependencies
npx chromatic --externals="public/**" --externals="src/styles/**"

# Use pre-built Storybook
npm run build-storybook
npx chromatic --storybook-build-dir=storybook-static

# Filter specific stories
npx chromatic --only-story-names="Button*"
npx chromatic --only-story-files="src/components/Button/**"

Storybook Configuration

Story-Level Configuration

// Button.stories.js
export default {
  title: "Components/Button",
  component: Button,
  parameters: {
    chromatic: {
      // Capture at multiple viewports
      viewports: [320, 768, 1200],
      // Delay before capture (ms)
      delay: 300,
      // Diff threshold (0-1)
      diffThreshold: 0.063,
      // Disable for this story
      disableSnapshot: false,
      // Pause animations
      pauseAnimationAtEnd: true,
    },
  },
};

export const Primary = {
  args: {
    primary: true,
    label: "Button",
  },
};

export const Loading = {
  args: {
    loading: true,
  },
  parameters: {
    chromatic: {
      // Extra delay for loading states
      delay: 1000,
    },
  },
};

// Disable snapshot for specific story
export const Interactive = {
  parameters: {
    chromatic: { disableSnapshot: true },
  },
};

Global Configuration

// .storybook/preview.js
export const parameters = {
  chromatic: {
    // Default viewports for all stories
    viewports: [375, 768, 1280],
    // Default delay
    delay: 200,
    // Diff threshold
    diffThreshold: 0.063,
    // Pause animations
    pauseAnimationAtEnd: true,
  },
};

Modes (Theme/Locale Testing)

// .storybook/modes.js
export const allModes = {
  light: {
    theme: "light",
  },
  dark: {
    theme: "dark",
  },
};

// .storybook/preview.js
import { allModes } from "./modes";

export const parameters = {
  chromatic: {
    modes: allModes,
  },
};

Interaction Testing

import { within, userEvent } from "@storybook/testing-library";
import { expect } from "@storybook/jest";

export const FilledForm = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Interact with the component
    await userEvent.type(canvas.getByLabelText("Email"), "test@example.com");
    await userEvent.type(canvas.getByLabelText("Password"), "password123");
    await userEvent.click(canvas.getByRole("button", { name: "Submit" }));

    // Chromatic captures snapshot AFTER interactions complete
    await expect(canvas.getByText("Success")).toBeInTheDocument();
  },
};

CI/CD Integration

GitHub Actions

name: Chromatic
on: push
jobs:
  chromatic:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for TurboSnap
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          onlyChanged: true  # TurboSnap
          exitZeroOnChanges: true
          autoAcceptChanges: "main"  # Auto-accept on main

GitLab CI

chromatic:
  image: node:20
  script:
    - npm ci
    - npx chromatic --project-token=$CHROMATIC_PROJECT_TOKEN --exit-zero-on-changes
  variables:
    CHROMATIC_PROJECT_TOKEN: $CHROMATIC_TOKEN

Generic CI

#!/bin/bash
npm ci
npx chromatic \
  --project-token="$CHROMATIC_PROJECT_TOKEN" \
  --only-changed \
  --exit-zero-on-changes \
  --auto-accept-changes="main"

TurboSnap (Smart Snapshots)

# Only snapshot stories affected by code changes
npx chromatic --only-changed

# Specify external dependencies that affect stories
npx chromatic --only-changed \
  --externals="src/styles/**" \
  --externals="public/fonts/**" \
  --externals=".storybook/**"
// Mark a story as always captured (even with TurboSnap)
export const CriticalComponent = {
  parameters: {
    chromatic: {
      disableTurboSnap: true,
    },
  },
};

Advanced Usage

Custom Viewports

export default {
  title: "Pages/Dashboard",
  parameters: {
    chromatic: {
      viewports: [320, 768, 1024, 1440, 1920],
    },
  },
};

Handling Dynamic Content

export const WithDynamicData = {
  parameters: {
    chromatic: {
      // Wait for content to load
      delay: 2000,
      // Increase diff threshold for minor rendering differences
      diffThreshold: 0.1,
    },
  },
};

// Use decorators to mock dates/random data
export default {
  decorators: [
    (Story) => {
      // Mock Date for consistent snapshots
      jest.useFakeTimers().setSystemTime(new Date("2024-01-15"));
      return <Story />;
    },
  ],
};

Ignoring Elements

/* Add to elements that should be ignored in diffs */
[data-chromatic="ignore"] {
  /* Chromatic will ignore changes in these elements */
}
export const WithAd = {
  decorators: [
    (Story) => (
      <div>
        <div data-chromatic="ignore">
          <AdBanner />
        </div>
        <Story />
      </div>
    ),
  ],
};

Configuration

# Environment variables
export CHROMATIC_PROJECT_TOKEN="chpt_xxxx"
export CHROMATIC_BRANCH="feature-branch"
export CHROMATIC_SHA="abc123"

# package.json script
{
  "scripts": {
    "chromatic": "chromatic --exit-zero-on-changes",
    "chromatic:ci": "chromatic --auto-accept-changes=main --only-changed"
  }
}

# chromatic.config.json
{
  "projectToken": "chpt_xxxx",
  "onlyChanged": true,
  "externals": ["src/styles/**"],
  "autoAcceptChanges": "main"
}

Troubleshooting

IssueSolution
Build fails to uploadCheck project token; verify Storybook builds successfully
Too many changes detectedUse TurboSnap (--only-changed); check for flaky animations
Animations causing diffsSet pauseAnimationAtEnd: true; add CSS to disable animations
Dynamic content causing diffsUse delay parameter; mock dates and random data
Fonts not loadingIncrease delay; add font CDN to --externals
TurboSnap not detecting changesEnsure fetch-depth: 0 in git checkout; check --externals
Slow buildsUse --only-changed; pre-build Storybook with --storybook-build-dir
Baseline mismatchAccept changes on main branch; use --auto-accept-changes