Skip to main content

Advanced usage

This page is your last stop before the API reference for trickier setups, scale/perf tuning, and production-hardening.
If you haven’t yet, first follow Quick Start and Integration with Storybook to get a green run, then return here to refine.

Handling dynamic content effectively

  1. Dynamic match level Prefer the Dynamic match level for pages with emails, timestamps, or randomized data. Combine with ignoreRegions/floatingRegions for animations and moving containers.

    Per-story example (CSF):

    export const PriceCard = {
    parameters: {
    eyes: {
    matchLevel: 'Dynamic', // tolerate fluctuating minor text
    },
    },
    };
  2. Ignore regions Use ignore regions for truly irrelevant content (ads/timestamps). Use floating regions when something moves within bounds (snackbars/toasts).

    export const WithToast = {
    parameters: {
    eyes: {
    ignoreRegions: [{ selector: '.timestamp' }],
    floatingRegions: [
    {
    selector: '.toast',
    maxUpOffset: 10,
    maxDownOffset: 10,
    maxLeftOffset: 40,
    maxRightOffset: 40,
    },
    ],
    },
    },
    };
  3. Make stories deterministic (pro tip)

    Eyes appends ?eyes-storybook=true to the story iframe. Branch off it to freeze dates/seeds.

    const params = new URL(window.location.href).searchParams;
    if (params.get('eyes-storybook')) {
    (window as any).__NOW__ = new Date('2025-01-01T00:00:00Z');
    Math.random = () => 0.42;
    }

Asynchronous UIs & interactions

If a story uses Storybook play (interactions), Eyes captures after the play function finishes. Then any waitBeforeCapture delay applies.

  • Prefer navigationWaitUntil: 'networkidle2' for UIs with long async work.
  • In interactions, favor explicit readiness markers (data-ready, aria-busy=false) over sleeps.
// applitools.config.js
module.exports = {
navigationWaitUntil: 'networkidle2',
waitBeforeCapture: '#ready', // or a global predicate as above
};

Network hygiene & performance

Trim noise and speed up cycles:

  • Block chatter:

    npx eyes-storybook --networkBlockPatterns "*/ads/*,*/analytics/*"

    Or in config:

    module.exports = { networkBlockPatterns: ['*/ads/*', '*/analytics/*'] };
  • Cache requests:

    module.exports = { browserCacheRequests: true };
  • Subset locally, go broad in CI:

    npx eyes-storybook --include /Button:.*/
  • Avoid unnecessary full-page capture: keep fully for pages/containers that truly need it.

Concurrency & throughput tuning

Balance speed vs. resources/quota.

  • Local tab fan-out (APPLITOOLS_CONCURRENT_TABS) Default is 3. Increase cautiously on small CI runners.
    export APPLITOOLS_CONCURRENT_TABS=5

Monorepos & multiple Storybooks

  • Use storybookConfigDir to point Eyes to the right .storybook per package.
  • Run separate batches per package to keep results focused.

Theming, locales, and feature flags

Drive stories with globals/args and run Eyes with different permutations using --include filters or multiple CI jobs; tag each run with properties (theme=dark, locale=fr‑FR, etc.).

Docker & CI hardening

If Chrome struggles in containers, set runInDocker: true or override puppeteerOptions.executablePath. Ensure enough shared memory and fonts are available. Prefer serving a static Storybook in CI, then run Eyes against its URL.

# GitHub Actions sketch
- run: npm run build-storybook
- run: npx http-server storybook-static -p 6006 &
- run: npx eyes-storybook -u http://localhost:6006 -e
env:
APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}

Root Cause Analysis (RCA) for encoded DOM/CSS

Provide a domMapping JSON to map hashed identifiers back to readable names:

module.exports = { domMapping: './dom-mapping.json' };
// { "abcd": "cls-button", "qwerty": "var-color" }