ScreenshotNeo

BlogHow-to

How to configure Happo for a React component library

Set up Happo with Storybook, choose useful visual coverage, and run selective checks in CI while keeping snapshot usage predictable.

By the ScreenshotNeo team4 October 202610 min read

To configure Happo for a React component library, install the happo development dependency, point a root-level happo.config.ts at the library’s Storybook configuration directory, and run the Happo CLI. Current Happo versions add the client runtime to the Storybook package they build, so the basic setup does not need a manual registration import. Run Happo on both pull requests and your default branch so selective pull request runs can compare against a maintained baseline.

Prerequisites

  • A working Storybook app in the repository.
  • Stories that render the React components and states you want to compare.
  • A Happo account and the credentials required by your CI setup.

This guide uses the current documented Storybook integration. The exact Storybook builder, output directory, and CI environment vary by repository, so align the paths and secrets with your actual setup.

1. Install Happo and configure Storybook

Install Happo as a development dependency using the package manager already used by the project:

npm install --save-dev happo
# or
pnpm add --save-dev happo
# or
yarn add --dev happo

Create happo.config.ts in the project root. The default Storybook configuration directory is .storybook; change configDir if yours is elsewhere.

import { defineConfig } from 'happo';

export default defineConfig({
  integration: {
    type: 'storybook',
    configDir: '.storybook',
  },
});

Happo’s current CLI inserts its client runtime into the Storybook package it builds. A manual import 'happo/storybook/register' is not needed for the basic setup. Older examples may show that import; the docs say it was required before Happo 6.19.1. The import can still be useful when using helpers such as theme switching or forced screenshots. See the Happo Storybook integration documentation for current syntax and details.

Add a package script so local development and CI call the same command:

{
  "scripts": {
    "happo": "happo"
  }
}

Run it locally:

npm run happo

Use pnpm run happo or yarn happo if that is the repository’s package manager. The command builds the configured Storybook integration and submits the visual run.

2. Match integration paths to the project

The default paths work for many repositories. Use the integration options when the Storybook setup has a custom layout or build pipeline:

Option What it controls When to adjust it
configDir Storybook configuration directory; default .storybook. Set it to the actual configuration folder in a package or monorepo.
outputDir Compiled Storybook output directory; default .out. Align it with the output your builder or prebuilt package produces.
staticDir Comma-separated list of static asset directories. Include the directories needed to resolve fonts, images, and other static files.
usePrebuiltPackage When true, tells Happo to use an existing package instead of building Storybook. Use when your pipeline already builds Storybook, and ensure outputDir points to that package.
previewOnly Builds the Storybook preview without the manager UI; documented default is true. Set to false if you need the built package’s manager UI, such as when downloading and browsing it locally.
navigatePerStory Loads each story in a fresh page rather than navigating between stories client-side. Enable when stories leak state into one another; expect slower runs.

For example, a project with a non-default Storybook config directory and existing build output could use:

import { defineConfig } from 'happo';

export default defineConfig({
  integration: {
    type: 'storybook',
    configDir: 'packages/ui/.storybook',
    outputDir: 'packages/ui/storybook-static',
    usePrebuiltPackage: true,
    previewOnly: true,
    navigatePerStory: false,
  },
});

Treat that as a shape example, not a universal monorepo configuration: the paths must match the directories your own build creates. Most integration options align with Storybook’s build options. Check the builder and output produced in your repository before changing them.

3. Choose stories that catch real regressions

A visual run is only as useful as the component states represented by its stories. Add named stories for states that matter to consumers of the library, where applicable:

  • Default, disabled, loading, and error states.
  • Open menus, dialogs, tooltips, and other interactive states.
  • Hover and keyboard focus states.
  • Long, localized, or unusually constrained content.
  • Light, dark, or branded themes when they alter rendering.
  • Responsive sizes that exercise meaningful layout changes.

Prefer stable, deterministic content. Avoid current timestamps, random values, animations that have not settled, and network-dependent data unless the story controls them. Use Storybook interaction tests to put a component into a state before capture when needed; behavioral assertions and visual comparisons answer different questions and work best as complementary checks.

Happo supports theme variants through the happo.themes story parameter, for example ['light', 'dark'], and provides a theme-switching helper through happo/storybook/register. Make sure the helper changes the same theme inputs that production uses, such as the actual theme provider or document attribute. Otherwise the screenshot may miss a real theme regression.

// Example Storybook story metadata; use the theme values supported by your app.
export const ButtonStates = {
  parameters: {
    happo: {
      themes: ['light', 'dark'],
    },
  },
};

Exclude a story or file from capture by setting parameters.happo = false when it is unstable or unsuitable for visual comparison. Keep exclusions narrow and intentional so they do not hide useful coverage.

4. Select browser, viewport, and run coverage

Decide coverage from your users’ environments and your library’s risk areas. A useful sequence is:

  1. Start with the browser coverage available on your Happo plan.
  2. Add browser engines that matter to the applications consuming the library.
  3. Choose viewport sizes around actual responsive breakpoints and known layout risks.
  4. Expand theme and state coverage for components whose rendering changes substantially.
  5. Review CI time and snapshot use before widening the matrix further.

Happo advertises rendering across Chrome, Firefox, Safari, Edge, and iOS Safari, but the browsers included depend on the plan. Check the Happo pricing page for current entitlements. More browser and viewport combinations improve coverage only when they test environments relevant to your consumers.

5. Run Happo in CI and maintain a baseline

Configure CI to run Happo on pull requests and on the main or default branch. Happo documents automatic detection for common CI providers including GitHub Actions, CircleCI, Travis CI, and Azure DevOps. Follow its CI documentation for provider-specific environment setup and credentials; the required YAML and secret names depend on your provider and account.

Running on the default branch maintains comparison data for later pull requests. Selective pull request runs can render only chosen stories and combine the new screenshots with matching baseline screenshots for a complete report. If no usable baseline exists, a partial run may not give the comparison you expect. Baseline updates can also be pending while a comparison is finalized.

For a large catalog, use Happo’s --only or --skip filters to narrow a run to named components or story files. Log the selected filter in CI so it is clear what was rendered. Excluded stories may still appear in reports through baseline comparisons; the newly rendered screenshots are what count as fresh captures. Malformed story metadata or unresolved filtering can cause a fallback to a full run, so inspect the run output when snapshot usage changes unexpectedly.

# Run all configured stories
npm run happo

# Restrict a run using Happo CLI filters
npx happo --only Button
npx happo --skip Experimental

Confirm exact filter syntax against the installed CLI version and your story naming. The examples show the documented filter flags; names must match the components or files as Happo sees them.

6. Estimate snapshot usage before expanding coverage

Happo defines one snapshot as one screenshot of one component variant in one browser. A practical estimate is:

variants × browsers × Happo runs per month = approximate monthly snapshots

For example, 40 variants across 3 browsers on 80 monthly runs would be about 9,600 snapshots before accounting for retries or changes in the run matrix. Count actual stories and theme variants rather than repository components alone.

Happo’s pricing page gives its own illustration: 50 components × 3 browsers × 100 monthly runs = 15,000 snapshots. The page currently lists a free plan with 5,000 snapshots per month in Chrome and says the free account pauses at quota until upgrade or the next cycle; paid overages are billed at the displayed rate. These quotas, prices, and browser entitlements can change, so verify the current pricing details before setting a budget.

Keep usage predictable by running focused pull request checks where appropriate, maintaining a complete default-branch baseline, limiting redundant browser combinations, and avoiding automatic retries that multiply captures without helping diagnose failures.

7. Keep visual and accessibility checks distinct

Happo says accessibility checks can run alongside screenshot testing. Treat an accessibility violation report and a visual diff as separate signals: a screenshot can look correct while a control is inaccessible, and a semantic accessibility issue may not create a visible pixel difference. Keep both in the review process when the project needs both kinds of coverage.

Troubleshooting

Symptom Likely cause Fix
Storybook config cannot be found configDir points to the wrong directory or the command runs from a different package root. Run the command from the configured project root and set configDir to the real Storybook directory.
Assets or fonts are missing The static asset directories are not included in the integration build. Set staticDir to the comma-separated asset directories used by the project and confirm they are available in CI.
Happo appears not to capture stories Storybook does not build, stories are excluded, or a filter matches no story names. Build Storybook independently, inspect parameters.happo, and verify --only/--skip values against the run output.
Story state carries into the next screenshot Client-side navigation reuses the page and application state leaks between stories. Set navigatePerStory to true; this isolates stories with a fresh page at the cost of speed.
Partial pull request report is incomplete or falls back to a full run The default branch has no usable baseline, story metadata is malformed, or filter resolution failed. Run Happo on the default branch, inspect metadata and filter logs, then retry the intended selection.
Unexpectedly high snapshot use Theme and browser variants, retries, or a filter fallback expanded the rendered set. Compare actual rendered variants and browsers with the monthly formula, check CI retries, and log selected filters.
Old registration snippet conflicts with current setup The repository follows instructions for Happo versions before 6.19.1. Use the current integration docs for the installed version. Add manual registration only when a helper requires it.
Screenshots differ on every run Stories rely on changing data, unfinished animations, or uncontrolled interaction state. Use deterministic fixtures, settle interactions before capture, and remove or control time- and network-dependent values.

Performance, reliability, and cost notes

  • Build cost: Storybook build time is part of the visual check. Reuse an existing package with usePrebuiltPackage when your pipeline already builds it, and align outputDir.
  • Isolation tradeoff: navigatePerStory: true can prevent state leakage but is slower because each story loads in a fresh page.
  • Baseline reliability: Default-branch runs keep selective pull request comparisons useful. Without baseline data, partial reports can be incomplete.
  • Quota: Every added story/theme variant and browser multiplies fresh screenshots. Estimate from actual variants and monthly CI runs, including retries.
  • Plan limits: Browser availability, quotas, prices, and overage behavior are plan details that may change; check Happo’s pricing page before expanding usage.

Or skip the browser setup

If you need a clean screenshot of a rendered page rather than a component-by-component baseline, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF; its options include CSS selector capture, viewport and device presets, custom CSS and JavaScript, and wait conditions. See the ScreenshotNeo API documentation for parameters and examples.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
  • Cookie banners are accepted and removed before capture; more than 60 known consent platforms, newsletter popups, and chat widgets can be removed, with each step configurable.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status.
  • An MCP server lets Claude, Cursor, and other MCP clients use screenshot, page-info, and PDF capture tools.
  • 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

FAQ

Do I need to add a Happo decorator to Storybook?

Not for the basic current integration. A decorator or preset is optional for inspecting Happo parameters or using helpers in Storybook itself; check version-specific guidance before adding older snippets.

Should every component have a screenshot?

Cover representative public states and meaningful rendering differences. A focused set of stable stories is more useful than many redundant variants.

Does a visual pass prove a component is accessible?

No. Visual diffs and accessibility checks detect different classes of problems, so use the checks your project requires as separate review signals.

Can I use Happo without Storybook?

This setup is specifically for Happo’s Storybook integration and assumes a working Storybook app. Use the integration documentation to choose the appropriate setup for a different application shape.

Sources