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.
npm install -D vitest jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom @testing-library/user-eventjsdom 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.
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.
// 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.
{
"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.
// 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.
// 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.
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.
// 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
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.
Frequently Asked Questions
Does Vitest need a separate jsdom install?
Can I keep using my existing Vite config?
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.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.