Happo for Vitest

It passes in Chrome. Does Safari agree?

Vitest’s Browser Mode already renders your components in a real browser, hovers them, and types into them. Hand Happo the element and it renders it in Chrome, Firefox, Edge, Safari, and on iPhone, and shows what your pull request changed.

Start free with 5,000 snapshots a month. No credit card needed.

AccountMenu.test.tsx
import { happoScreenshot } from 'happo/vitest/browser';

test('open', async () => {
  render(<AccountMenu />);
  await userEvent.click(screen.getByRole('button'));

  await happoScreenshot(screen.getByRole('menu'), {
    component: 'AccountMenu',
    variant: 'open',
  });
});
The CI run

npx happo -- vitest run

Tests 212 passed (212)

Happo 3 diffs need reviewSafari, iOS Safari

Review

The gap

It works. It just doesn’t look right.

A component test checks what the component does: it renders, it shows the right text, the click calls your handler. It runs in a real browser, but it never looks at what that browser drew, or at any other browser. That’s what Happo is for.

What CI checked

  • expect(card).toBeVisible()
  • expect(price).toHaveTextContent('€412')
  • await userEvent.click(reserve)
  • expect(onReserve).toHaveBeenCalled()

All checks passed

What Happo saw1 diff

BookingCard / defaultSafari

The card rendered and the click called onReserve, so every test passed. In Safari, the price wrapped under the button.

While CI functional tests are helpful they can't catch visual issues like layout shifts or SVG chart rendering. Happo has become essential to our testing strategy ensuring high-quality products with minimal effort.

Samuel WeirichPILOS

Setup

Add Happo in an afternoon.

You keep your Vitest setup. Happo adds a step to it, and every pull request gets a status that links to its diffs.

  1. Install Happo

    Terminal
    npm install --save-dev happo
  2. Add the plugin

    vitest.config.ts
    import { playwright } from '@vitest/browser-playwright';
    import { happoVitestPlugin } from 'happo/vitest';
    import { defineConfig } from 'vitest/config';
    
    export default defineConfig({
      plugins: [happoVitestPlugin()],
      test: {
        browser: {
          enabled: true,
          provider: playwright(),
          instances: [{ browser: 'chromium' }],
        },
      },
    });

    One browser instance is enough. Happo renders each screenshot again in every browser you pick.

  3. Take a screenshot in a test you have

    AccountMenu.test.tsx
    import { render, screen } from '@testing-library/react';
    import { happoScreenshot } from 'happo/vitest/browser';
    import { test } from 'vitest';
    import { userEvent } from 'vitest/browser';
    
    import AccountMenu from './AccountMenu';
    
    test('open', async () => {
      render(<AccountMenu />);
      await userEvent.click(screen.getByRole('button'));
    
      await happoScreenshot(screen.getByRole('menu'), {
        component: 'AccountMenu',
        variant: 'open',
      });
    });

    Pass any element: one from Testing Library’s screen, or a locator from page in vitest/browser.

  4. Pick your browsers

    happo.config.ts
    import { defineConfig } from 'happo';
    
    export default defineConfig({
      integration: {
        type: 'vitest',
      },
      targets: {
        chrome: { type: 'chrome', viewport: '1024x768' },
        safari: { type: 'safari', viewport: '1024x768' },
        iphone: { type: 'ios-safari' },
      },
    });
  5. Wrap vitest run in CI

    Terminal
    npx happo -- vitest run

    Happo keeps the screenshots of each test that passes, renders them in each browser in parallel, and posts a status on the pull request.

The full Vitest guide

Built for Vitest

Right where your components are tested.

There’s no second suite to keep up. Happo’s screenshots ride along with the component tests your team already writes.

  • One plugin, one call

    happoVitestPlugin goes in your Vitest config, and happoScreenshot takes the element your test already has, from Testing Library or a page locator.

  • Hover, focus, and pressed

    Hover a button or tab to it with userEvent, turn on applyPseudoClasses for your targets, and Happo shows that state in every browser.

  • Only tests that pass

    Happo keeps a test’s screenshots once it passes. A failing test’s are dropped, and a retried test keeps only the attempt that passed.

  • Out of the way locally

    Happo runs when CI wraps vitest run. In watch mode and in your editor, happoScreenshot does nothing.

Happo allows us to make changes to components used on hundreds of screens and upgrade core UI dependencies with confidence. It has been a big productivity boost over our previous workflow.

Phil GlassMonzo

In every run

Your Vitest suite, in every browser.

Vitest runs your tests in one browser. Happo takes what they render, draws it again in each browser you pick, and compares each one with your base branch.

  • Chrome
  • Firefox
  • Edge
  • Safari
  • iOS Safari
  • Every browser your users have

    Each screenshot renders in the browsers you pick: Chrome, Firefox, Edge, Safari, and Safari on iPhone and iPad, at any viewport, in light mode and dark.

    Browsers and targets
  • Accessibility in the same run

    Add an accessibility target and every screenshot is checked too, with each violation pointed out on a screenshot. New ones block approval; the ones you already have don't.

    Accessibility testing
  • Animations, frame by frame

    Spinners, toasts, and transitions get stepped to exact moments and compared frame by frame, so a change to the motion is a diff like any other.

    Animation testing
  • Only real changes

    Rendering noise under your threshold is filtered out, every diff is deterministic, and a flake you report once stays ignored.

    Keeping diffs stable
  • A status on every pull request

    Happo compares with your base branch and posts a status that links to the review. It turns green when someone signs off.

    Continuous integration
  • Diffs your agents can read

    Claude Code and Cursor read the same diffs through Happo’s MCP server, fix what they broke, and hand you a pull request that already passes.

    The MCP server

FAQ

Questions, answered

What is Vitest visual regression testing?

Visual regression testing takes screenshots during your Vitest tests and compares them with the same screenshots on your base branch, so a change to how a component looks shows up as a diff before you merge.

Why not use Vitest’s own screenshot assertions?

They work well for a few components in one browser. The reference images live in your repository, they depend on the browser and operating system that took them, and you review changes as image files. Happo renders in real browsers on its own machines, so results don’t change with your CI machine, and you review every diff in one place, with a status on the pull request.

Do my tests have to run in Browser Mode?

Yes. Happo takes the element from a real browser, so the tests you add screenshots to run in Vitest’s Browser Mode, with the Playwright or WebdriverIO provider. Tests that run in jsdom or happy-dom can stay as they are.

Do my tests have to run in every browser?

No. Run your tests the way you do today, in one browser. Happo takes the element at each screenshot and renders it in every browser you pick, including Safari and iOS Safari.

Does Happo test accessibility too?

Yes, with no new tests to write. Add an accessibility target and each screenshot is checked for accessibility violations in the same run. It’s included on every plan.

How does Happo keep screenshots from being flaky?

Happo waits for fonts and images, holds animations still unless you ask it to test them, and filters out rendering noise below a threshold you set. When a diff is a flake, report it once and Happo ignores it from then on.

Does it run on every pull request?

Yes. Wrap vitest run with npx happo in CI, and every pull request gets a status that links to its diffs. Sharded runs come together in one report. It works with GitHub Actions, CircleCI, Azure DevOps, and the rest.

Test it once. See it everywhere.

Add Happo to your Vitest suite this afternoon. Your next pull request shows what it changed, in the browsers your users have.

Start free with 5,000 snapshots a month. No credit card needed.

Need help?