How to Test React Components with Vitest

Wire Vitest, jsdom, React Testing Library, and jest-dom together so you can write and run fast component tests in a Vite React project.

7 min read

Vitest is a Vite-powered test runner that fits React projects already built with Vite. This setup wires up jsdom as the fake browser, React Testing Library for rendering and queries, and jest-dom for readable assertions, so you can test React components with Vitest in one command.

Vitest currently requires Vite 6 or newer and Node 20 or newer. The examples below assume a Vite React project.

Install the packages

Install the runner, the DOM environment, and the Testing Library packages together. The command places everything in dev dependencies.

bashbash
npm install -D vitest jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom @testing-library/user-event

jsdom is a JavaScript implementation of the DOM that runs in Node, and it is installed separately because Vitest does not bundle a browser environment. The happy-dom package is a lighter alternative that you can swap in the same way. Choose one DOM environment and keep it consistent across the whole suite.

Configure the test environment

Vitest reads the Vite config by default, so the React plugin that already transforms JSX keeps working in tests. If your project does not have that setup yet, follow how to create a React app with Vite. Add a test block to the config file.

index.tsindex.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
 
export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    globals: true,
    setupFiles: ["./src/setupTests.js"],
  },
});

The environment option makes tests run against jsdom instead of Node. The globals option lets tests use the test and expect functions without importing them, and it is also required for React Testing Library auto cleanup to work.

Create the setup file that registers the jest-dom matchers.

index.jsindex.js
// src/setupTests.js
import "@testing-library/jest-dom/vitest";

The vitest entry point attaches matchers such as toBeInTheDocument and toHaveTextContent to the expect function. Without it, those matchers are undefined.

Add a test script to package.json so one command runs the suite.

jsonjson
{
  "scripts": {
    "test": "vitest"
  }
}

Write and run a component test

A small status component gives the test something visible to assert. This one shows a different word for each prop value.

App.jsxApp.jsx
// src/Status.jsx
export default function Status({ isOnline }) {
  return (
    <p role="status">{isOnline ? "Online" : "Offline"}</p>
  );
}

The test renders the component and checks the text that a user would read. If you have not used the library before, the React Testing Library tutorial explains the query style.

App.jsxApp.jsx
// src/Status.test.jsx
import { render, screen } from "@testing-library/react";
import Status from "./Status";
 
test("shows Online when the user is online", () => {
  render(<Status isOnline />);
 
  expect(screen.getByText("Online")).toBeVisible();
});

Run the suite with npm test. Vitest prints the passing file and test count.

The visible result is a green summary line, not a browser window, because jsdom simulates the page in memory. In watch mode it reruns only the files affected by a change, which keeps the loop fast.

Choose between globals and explicit imports

With globals enabled, the test and expect functions are available without imports. If you prefer explicit imports, turn globals off and import them in every file.

App.jsxApp.jsx
import { test, expect } from "vitest";
import { render, screen } from "@testing-library/react";

When globals are off, React Testing Library cannot auto-clean between tests, so you must register cleanup manually in a setup file before any test runs.

index.jsindex.js
// src/setupTests.js
import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";
 
afterEach(() => {
  cleanup();
});

Cleanup unmounts each rendered tree after a test so DOM from one test does not leak into the next.

Fix common setup failures

If tests throw a document not defined error, the environment is still node. Set the environment to jsdom and install jsdom.

If matchers such as toBeVisible are undefined, the setup file is not loading. Check the setupFiles path and that the file imports the vitest entry of jest-dom.

If a test file named with .test.jsx is ignored, verify the file name contains .test. or .spec., which Vitest matches by default. Files without either marker are not collected as tests, even when they import the test function.

For tests that reach the network, replace real requests with mocks. Follow how to mock API requests with MSW for the standard approach.

What to learn next

The runner is ready. Learn the query style that keeps tests readable in the queries guide, then practice interactions with user events. Both articles build on the setup you just completed.

Rune AI

Rune AI

Key Insights

  • Vitest requires Vite 6 or newer and Node 20 or newer.
  • Install vitest and jsdom, then set test.environment to jsdom.
  • Add @testing-library/react and @testing-library/jest-dom for queries and matchers.
  • Import @testing-library/jest-dom/vitest in a setup file listed in setupFiles.
  • Enable globals or import test and expect from vitest in each file.
RunePowered by Rune AI

Frequently Asked Questions

Does Vitest need a separate jsdom install?

Yes. Vitest ships without a DOM environment. Install jsdom or happy-dom as a dev dependency and set test.environment to it.

Can I keep using my existing Vite config?

Yes. Vitest reads vite.config.ts by default, so your React plugin and aliases apply to tests. Add the test block to the same file or create a dedicated vitest.config.ts.

Conclusion

Vitest gives a Vite React project a fast test runner with the same config it already uses. Add jsdom, React Testing Library, and jest-dom, then run vitest to see component tests pass.