Make Storybook stories deterministic for UI Verify so captures stop coming back "changed" without a real change (flaky diffs). Use when setting up story-level visual tests or debugging a story that di
UI Verify renders each story as a baseline. Because a story is an isolated component in a controlled harness, you get for free the things that make real-page capture hard: no page scroll, no A/B / analytics / chat / consent scripts, no lazy-load-on-scroll races. That isolation is the point — the determinism work here is narrow. If you reach for scroll-settling or third-party stubbing, you're fighting a problem Storybook already removed; that's a real-page concern (see `playwright-visual-testing`).
At 1,276 words, this large prompt gives your agent specialized testing & qa expertise with structured patterns and output formats. Copy it into CLAUDE.md, .cursorrules, or Copilot instructions.
UI Verify renders each story as a baseline. Because a story is an isolated component in a controlled
harness, you get for free the things that make real-page capture hard: no page scroll, no A/B / analytics
/ chat / consent scripts, no lazy-load-on-scroll races. That isolation is the point — the determinism
work here is narrow. If you reach for scroll-settling or third-party stubbing, you're fighting a
problem Storybook already removed; that's a real-page concern (see playwright-visual-testing).
UI Verify's capturer also neutralizes these automatically — do NOT hand-fix them:
prefers-reduced-motion: reduce — emulated, so any component that honors it renders its calm state.Math.random — seeded before your app code runs (a shuffle/jitter driven by Math.random is<img> loading — waited for before capture (the fonts your story actually loads; seepreview.ts setup below).
So the checklist below is only the remainder — what lives inside your app and can't be fixed from
outside it. And like Vitest browser mode (vitest-visual-testing) and unlike a real page, a story has
no SSR — no server-rendered random pick to reconcile.
The headline: freeze the data. Once the list above is off the table, the one thing left that floods a
story suite with false "changes" is live/dynamic data — star counts, follower counts, contributor
lists, tiles, timestamps. Give every story static args / fixtures and that entire class disappears at
the source: static data can't churn run-to-run, so there is nothing to diff. This is the single
highest-value determinism step here — do it first (step 1), and most stories need nothing else.
Load your app's global CSS + fonts in .storybook/preview.ts. A story renders an isolated component,
so anything your real app loads in its entry/layout — the global stylesheet, an icon-font CSS
(bootstrap-icons) — has to be imported in preview.ts (which loads once for every story) or the story
captures with fallback fonts / tofu glyphs:
// .storybook/preview.ts
import 'bootstrap-icons/font/bootstrap-icons.css';
import '../src/styles/globals.css';Import fonts from a package or a same-origin asset (not a CDN <link> in preview-head.html), so the
archive captures the bytes. The capturer waits for these like any other font — but only ones the story
actually loads; it can't declare a font you never imported. Rule of thumb: if a story renders right in
your local Storybook, its fonts are already in the build.
Point UI Verify at your built stories:
npm run build-storybook && uiverify upload --static-dir storybook-staticA story fed live/dynamic data is flaky by construction: the stars, followers, contributor list, tile
order, and timestamps move between runs, so the diff lights up with no code change. Give every story
static args/fixtures and the whole class is gone. Never let a story hit a real backend.
Concrete — fixed, ordered, complete data:
args on the story;msw-storybook-addon](https://storybook.js.org/addons/msw-storybook-addon)One component per story file, every variant × state in one story where you can — cheaper and easier
to eyeball than N near-identical stories. Keep each in its own file so unchanged ones are skipped,
and add a path filter so the visual job only runs on UI PRs.
The one thing the capturer deliberately does not do (freezing time breaks entry animations). Any
component that reads the clock — a relative timestamp, a date picker defaulting to "today", a chart's day
axis — drifts every run. Pin it with
[storybook-addon-mock-date](https://www.npmjs.com/package/storybook-addon-mock-date) (Storybook 10+),
which mocks Date per story:
// .storybook/main.ts
addons: ['storybook-addon-mock-date'],
// .storybook/preview.ts — a fixed date for every story (meta- or story-level overrides it)
export default { parameters: { mockingDate: new Date('2020-01-01T00:00:00Z') } };Pass a Date, a millisecond timestamp, or an ISO string as mockingDate; the most specific value
(story > meta > preview) wins, so a single story can pin its own "today". On older Storybook, install
[@sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) in a decorator instead
(install({ now: FROZEN_NOW })) — it covers Date, Date.now, and timers in one call.
A CSS/WAAPI/finite animation is handled for you (above). What's left is an infinite JS loop that
never has a final frame — framer-motion pulsing dots, a Lottie loop, an autoplay spinner, or a<canvas> / requestAnimationFrame loop (which no media query can reach). Two fixes:
prefers-reduced-motion: reduce, so makereducedMotion: "never"); opt in:
// .storybook/preview.tsx decorator
<MotionConfig reducedMotion="user"><Story /></MotionConfig>
or gate the loop yourself with useReducedMotion(). One line, and it's good app behavior anyway.UIVerify marker on the user-agent and a window.__UI_VERIFY__ global, so a component can branch:
export const isUIVerify = () =>
(typeof navigator !== 'undefined' && navigator.userAgent.includes('UIVerify')) ||
(typeof window !== 'undefined' && '__UI_VERIFY__' in window);
<RadarChart isAnimationActive={!isUIVerify()} />
Prefer pausing at the end frame, not the start. For a hand-rolled <canvas> rAF loop the same
branch applies: if (isUIVerify()) drawOneStaticFrame(); else startRaf(); in the component's effect.Math.random randomnessMath.random is seeded for you, but crypto.randomUUID(), a uuid library, or faker are not. Use
fixed fixtures for anything that ends up on screen (an id in the DOM, a faker name), or set a fixed faker
seed.
A JS-measured layout that reflows or reorders on its own (packing driven by measured size, a shuffled
list) can vary run-to-run even with identical content. Force a deterministic variant in the story (a
fixed order/size), or mask the region (step 6).
Last resort for content that is supposed to differ and can't be pinned at the source (a third-party
embed, a map, an ad slot, a live feed): add data-uiverify-ignore to the element. UI Verify paints
its box solid black on every capture, so its content never reaches the diff. The box's size and position
still diff (give it a fixed size), and nothing inside it is checked, so mask only what you can't pin and
fix what you own. [Docs](https://uiverify.ai/docs/ignore-regions).
<aside data-uiverify-ignore style={{ width: 320, height: 256 }}><AdSlot /></aside>Math.random / waiting on fonts by hand → wasted effort; thecrypto/uuid/faker or a bare Date.now() → fixtures + freeze the clock.Storybook Visual Testing is a free testing & qa skill for AI coding agents. Make Storybook stories deterministic for UI Verify so captures stop coming back "changed" without a real change (flaky diffs). Use when setting up story-level visual tests or debugging a story that di. It provides a specialized system prompt that configures your agent with testing & qa expertise.
Copy the system prompt below and paste it into your CLAUDE.md file or Claude Code's custom instructions to activate Storybook Visual Testing.
Storybook Visual Testing is compatible with Claude Code, Cursor, GitHub Copilot, Windsurf, OpenClaw, Cline, and any AI agent that supports custom system prompts or .cursorrules files.
Yes, Storybook Visual Testing is completely free and open source. The full source is available on GitHub at https://github.com/uiverify/uiverify/blob/main/packages/skills/skills/storybook-visual-testing/SKILL.md. You only need a subscription to the AI agent you use it with.
Weekly roundup of top Claude Code skills, MCP servers, and AI coding tips.