[React] Introducing Vitest to a Jest Project - View Test Separation Strategy

2026-03-09 hit count image

Learn how to add Vitest to an existing Jest-based React project by separating View tests and logic tests into different runners. Covers setup in a monorepo environment and CI integration.

react

Overview

I’d like to share our experience of introducing Vitest to a React project that was previously using Jest for all tests. Rather than migrating everything at once, we adopted a strategy of using Vitest for View (component) tests and keeping Jest for Actions/logic tests.

Why Add Vitest

Jest has long been the standard testing framework in the React ecosystem, but it has some limitations.

  • Dual configuration with Vite: When building product code with Vite but running tests with Jest, separate transform configurations like ts-jest and jest-svg-transformer are required.
  • import.meta.env support: Vite projects access environment variables via import.meta.env, but Jest requires a separate package like ts-jest-mock-import-meta.
  • Snapshot test maintenance cost: Jest snapshot tests break easily with any HTML structure change, and large snapshot files are difficult to review.

Vitest is a Vite-native testing framework that fundamentally solves these problems.

  • It uses Vite’s module resolution and transform pipeline directly, eliminating the need for separate transform configurations.
  • It natively supports import.meta.env.
  • Using happy-dom provides a faster DOM environment than jsdom.

Strategy: Separating Test Runners

Migrating all tests at once would be risky, so we separated the runners based on test targets.

Test TargetRunnerReason
View (component) tests (under view/)VitestMatches Vite environment, natural CSS/SVG handling
Actions/Controller/Utils/Selectors testsJestLeverage existing test assets, gradual migration

This separation provided the following benefits:

  • Safely introduce Vitest without touching existing logic tests
  • Remove Jest’s complex transform configurations (ts-jest-mock-import-meta, dotenv, etc.) from View tests
  • Lay the groundwork for eventually consolidating all tests under Vitest

Vitest Environment Setup

Let’s walk through how to add Vitest to the project step by step.

Package Installation

Run the following command to install Vitest and related packages.

yarn add -D vitest @vitest/ui happy-dom

Here’s what each package does:

PackageRole
vitestTest runner (provides Jest-compatible API)
@vitest/uiView test results in a browser UI
happy-domLightweight DOM environment (jsdom alternative, faster execution)

happy-dom is a much faster DOM implementation than jsdom. It’s well-suited for View component rendering tests and supports most DOM APIs.

Adding package.json Scripts

Add the following scripts to your package.json file for running Vitest.

{
  "scripts": {
    "test": "TZ=Asia/Tokyo jest --watchAll",
    "test:ci": "TZ=Asia/Tokyo jest --ci --bail",
    "test:view": "TZ=Asia/Tokyo vitest",
    "test:view:ci": "TZ=Asia/Tokyo vitest run"
  }
}
  • test:view: Run View tests in watch mode during local development
  • test:view:ci: Run once and exit in CI environments

The existing test and test:ci scripts continue to use Jest as-is. TZ=Asia/Tokyo fixes the timezone for consistency in date/time-related tests.

Modifying Jest Configuration

We need to modify the Jest configuration so it no longer runs View tests. Open jest.config.js and update it as follows.

// jest.config.js
export default {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  testMatch: [
    '**/controller/**/*.test.ts',
    '**/actions/**/*.test.ts',
    '**/utils/**/*.test.ts',
    '**/selectors/**/*.test.ts',
  ],
  testPathIgnorePatterns: ['/node_modules/', '/view/'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '^.+\\.svg$': 'jest-svg-transformer',
    '\\.(css|scss)$': 'identity-obj-proxy',
  },
  setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
  clearMocks: true,
};

Here are the key changes:

  • Removed dotenv import: Since Vitest uses Vite’s automatic environment variable handling, it’s no longer needed for Actions tests in Jest either.
  • Added testMatch: Restricted test execution to only the controller/, actions/, utils/, and selectors/ directories.
  • Added /view/ to testPathIgnorePatterns: Explicitly excludes files under view/.
  • Removed transform configuration: With ts-jest-mock-import-meta no longer needed, the entire transform configuration was simplified.

By narrowing Jest’s test scope this way, View tests won’t run under Jest, preventing conflicts between the two runners.

If you’re interested in how to set up Jest for testing in a Vite + React + TypeScript project, check out [Vite] Setting Up a Test Environment for a TypeScript-based React Project.

Cleaning Up Existing View Tests

Along with introducing Vitest, we cleaned up the existing Jest-based View tests.

Removing Snapshot Tests

Most existing View tests were written as Jest snapshot tests like the following.

// Before: Jest snapshot test
import { render } from '@testing-library/react';
import { ItemPanel } from '.';

test('render well', () => {
  const { container } = render(
    <ItemPanel
      label="label"
      value="value"
      showButton
      buttonLabel="Edit"
      helpText="help"
    />
  );
  expect(container).toMatchSnapshot();
});

These snapshot tests had the following problems:

  • High maintenance cost: Even a small change in HTML structure breaks the snapshot, requiring review of 61+ line snapshot diffs.
  • Not meaningful verification: It only confirms “the HTML is the same as before” without verifying whether it actually looks correct to users.
  • Manual Mock complexity: Complex Mock files had to be written in __mocks__/ directories to decouple component dependencies.

We deleted these snapshot files (__snapshots__/) and __mocks__/ directories, preparing the groundwork for writing new tests in Vitest.

// After: Ready for new Vitest tests
describe('ItemPanel', () => {
  test('test', () => {
    // Write test
  });
});

This PR resulted in +1,139 lines added and -10,134 lines deleted, cleaning up a massive amount of snapshot and Mock code.

CI Integration (GitHub Actions)

We added a Vitest execution step to the existing CI workflow.

# .github/workflows/check_code_service_a.yml (before)
- name: Test
  run: yarn test:ci:serviceA -- --shard=${{ matrix.shard }}/10
# .github/workflows/check_code_service_a.yml (after)
- name: Test actions
  run: yarn test:ci:serviceA -- --shard=${{ matrix.shard }}/10
- name: Test view
  run: yarn test:view:ci:serviceA -- --shard=${{ matrix.shard }}/10

The changes are straightforward:

  • Renamed the existing Test step to Test actions (runs Jest)
  • Added a Test view step (runs Vitest)
  • Both steps use the --shard option to distribute tests in parallel

Since Vitest supports the --shard option just like Jest, we can leverage the existing CI parallel execution strategy as-is.

Summary

This article introduced how to gradually introduce Vitest to an existing Jest project.

ItemDetails
Introduction strategySeparate View tests (Vitest) from logic tests (Jest)
Vitest environmentvitest + happy-dom + @vitest/ui
Jest config changesExclude View tests with testMatch and testPathIgnorePatterns
Existing test cleanupDeleted snapshot tests and manual Mocks (-10,134 lines)
Removed unnecessary depsDeleted dotenv, ts-jest-mock-import-meta
CI integrationAdded Test view step to GitHub Actions, shard parallel execution

The key to introducing new tools is “don’t change everything at once”. By separating runners based on test targets, you can maintain existing test assets while enjoying the benefits of new tools. Eventually, migrating logic tests to Vitest as well will allow you to completely eliminate the Jest dependency.

For more on the test strategy planning process and tool selection in a monorepo environment, check out [React] Common Component Testing Strategy in a Monorepo. For building a VRT (Visual Regression Testing) environment with Vitest, see [React] Building Component VRT + Accessibility Testing with Vitest.

Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!

App promotion

You can use the applications that are created by this blog writer Deku.
Deku created the applications with Flutter.

If you have interested, please try to download them for free.



SHARE
Twitter Facebook RSS