React

Vitest: setting up a React project in ten minutes

Vitest joins a React project with three packages and one block in vite.config.js: jsdom, jest-dom and a single command for CI.

Key points

  • Three packages and one test block in vite.config.js are enough to start.
  • setupFiles loads jest-dom once for the whole suite.
  • npx vitest run is the CI form; npx vitest stays in watch mode.

A React project moves to Vitest with three packages and one config block: jsdom as the environment, jest-dom as the setup and a single command for local runs and CI.

The packages

npm i -D vitest jsdom @testing-library/react @testing-library/jest-dom

Vitest, Vite and jsdom ship under the MIT licence. The config lives in vite.config.js: no second bundler and no separate file.

The config block

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    globals: true,
    setupFiles: "./src/test/setup.js",
  },
});

Only Vitest reads the test block; Vite skips it in a build. setupFiles loads jest-dom once for every test, instead of importing it in each file.

The setup file

import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";

afterEach(() => cleanup());

The jest-dom import adds matchers such as toBeInTheDocument; with globals: true the test files skip importing test and expect.

The first test

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { LoginForm } from "./LoginForm";

test("mostra l'errore con email vuota", async () => {
  render( {}} />);
  await userEvent.click(screen.getByRole("button", { name: "Accedi" }));
  expect(screen.getByRole("alert")).toHaveTextContent("Email obbligatoria");
});

The npx vitest run command runs once and exits with zero or one: that is the CI form, while npx vitest keeps watching.

Live records in the catalogue

Three components worth a first suite: login-form, input and multistep-form. To browse by category start from 111,985 components.

The limitation

jsdom applies no CSS and computes no layout: a green test does not tell you whether the component is visible. That takes a real browser, with Playwright.

Where to continue

The five-test picture is in Testing Library: five tests; how to read documentation is in React documentation; the measured comparison of React libraries is in React libraries 2026.

Frequently asked questions

How long does the setup take?

Ten minutes on a Vite project: three packages, one config block and one setup file.

Do I need jsdom?

Yes for React components, which use the DOM. Pure functions run in the node environment.

Does Vitest replace Jest?

In practice yes: the same test API, but it runs on Vite and reads the project config.

How does it run in CI?

With npx vitest run: it runs once, leaves no watcher behind and returns an exit code.

Can tests sit next to components?

Yes, that is the default convention: Vitest looks for files with a test or spec suffix.