Storybook
One command opens every story of a built Storybook in Chromium, takes a screenshot at each width and uploads them.
Install
The capture uses the Playwright in your project and needs Storybook 7 or newer, which writes the index.json the CLI reads.
npm install -D stateofpixel playwright
npx playwright install chromiumRun it on CI
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx storybook build
- run: npx stateofpixel storybook storybook-static --viewports 375,1280Keep id-token: write and the push trigger on main from the Quickstart.
Each story is captured at every width in --viewports and named Title/Name [chromium 1280], like Button/Primary [chromium 375].
Pick stories and wait for them
npx stateofpixel storybook storybook-static \
--include "Components/**" \
--exclude "**/Playground" \
--tag snapshot \
--wait-for-selector "[data-ready]" \
--delay 200 \
--device-scale-factor 2| Flag | Default | What it does |
|---|---|---|
--viewports <widths> | 1280 | Comma separated viewport widths. |
--include <glob> | Only stories whose title/name match. | |
--exclude <glob> | Skip stories whose title/name match. | |
--tag <tag> | Only stories with this tag. | |
--wait-for-selector <selector> | #storybook-root > * | Wait for this before each screenshot. |
--delay <ms> | 0 | Wait this long before each screenshot. |
--device-scale-factor <n> | 1 | Pixels per CSS pixel, like 2 for retina. |
Each story opens in Chromium at a height of 720px, with reduced motion, grayscale text anti-aliasing and the device scale factor from --device-scale-factor, and the screenshot covers the full page. A story has 15 seconds to load and to match --wait-for-selector. After that, the CLI waits for the story's play function to finish, for its requests, Web Worker replies and the network to go idle and for images to load, including lazy ones, stops GIFs, videos and SVG animations, and takes screenshots until two in a row match. A story that keeps changing gets its last screenshot and a warning. When a story does not render, the command lists it and stops before anything uploads.
In --include and --exclude, * stays inside one segment of Title/Name and ** crosses segments.
--tag keeps only stories that have that tag in their tags.
With --device-scale-factor 2, a screenshot has twice the width and height in pixels. Each image can be up to 25 MB, so a tall story at 2 may go over it. See Limits.
storybook also takes every flag of upload, like --build-name, --shard and --threshold. With --shard 2/4, it captures only every fourth story, starting at the second, so each CI job takes its share. See the CLI reference. Use --dry-run to capture and print the plan without uploading.
Stories that load data or fonts late are the usual cause of noisy diffs. Stable screenshots has the fixes.
Capture with Storybook globals
--globals sets Storybook globals for every story, in the same key:value;key:value form as the globals URL parameter. To capture each value of a global, run once per value with its own --build-name, so each one gets its own baselines:
npx stateofpixel storybook storybook-static --build-name storybook
npx stateofpixel storybook storybook-static --build-name storybook-dark --globals theme:darkUse your Chromatic parameters
With --chromatic, the CLI loads the built Storybook, reads each story's parameters.chromatic and captures what Chromatic captures:
- Stories with
disableSnapshot: trueordisable: trueare skipped. - Each mode in
modesis one screenshot named after the mode, likeButton/Primary [chromium dark 1280px]. Modes withdisable: trueare skipped. - A mode's
viewportsets the width, in pixels or as the name of a viewport inparameters.viewportwhose width is in pixels. Withoutviewport, the mode uses the first width in--viewports. The mode's other keys, excepta11y, are passed as globals and override--globals. - Stories without modes use
viewportsfromparameters.chromatic, or--viewports. delayreplaces--delayfor that story.
npx stateofpixel storybook storybook-static --chromaticParameters and tags come from Storybook at runtime, so values set through shared objects or helper functions count too. --include, --exclude and --tag still apply on top. diffThreshold is not read, so use --threshold for the whole build.