Skip to main content

Vitest React Testing: Quick Setup Guide

Vitest is the fastest way to test React code in 2026. Unlike Jest, which relies on Node's module transpilation, Vitest runs tests using Vite's native ES module pipeline, delivering test execution 3–5x faster on medium-to-large codebases (Vitest benchmark suite, 2026). Setting up Vitest with React takes one command and a two-minute config—here's exactly how.

Install Vitest and Browser Utilities

The fastest route is to install Vitest alongside jsdom, which simulates a browser environment for component tests.

npm install -D vitest jsdom @vitest/ui

What each package does: vitest is the test runner. jsdom is a Node-based DOM emulator (your React components render into a fake document object). @vitest/ui is optional—it opens a beautiful web dashboard for test results in your browser.

If you're using a Vite project already (which you should be for React 2026), Vitest auto-detects your vite.config.ts and reuses your alias and resolver config. If you don't have Vite, install it:

npm install -D vite @vitejs/plugin-react

Create vitest.config.ts

Add a vitest.config.ts file to your project root:

import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
},
});

Field explanation:

  • globals: true — Vitest auto-imports describe, it, expect, etc. so you don't write imports in every test.
  • environment: 'jsdom' — Simulates a DOM. Use 'node' for API/utility tests (no DOM needed).
  • setupFiles — A file that runs once per test session (we'll use it for React Testing Library setup next).

Create vitest.setup.ts

Create a setup file to configure React Testing Library:

import { expect, afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';

afterEach(() => {
cleanup();
});

// Optional: Add custom matchers
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`,
pass: true,
};
} else {
return {
message: () => `expected ${received} to be within range ${floor} - ${ceiling}`,
pass: false,
};
}
},
});

The cleanup() call after each test unmounts React components and removes DOM artifacts—critical for test isolation. Custom matchers (like toBeWithinRange) let you write more readable assertions tailored to your domain.

Update package.json Scripts

Add test commands to your package.json:

{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}

Now npm test runs tests in watch mode (files re-test on save). npm run test:ui opens the web dashboard. npm run test:coverage generates a coverage report (covered in later articles).

Verify the Setup Works

Create a simple test file to confirm everything wires together. Create src/Button.tsx:

export function Button({ label, onClick }: { label: string; onClick: () => void }) {
return <button onClick={onClick}>{label}</button>;
}

Then src/Button.test.tsx:

import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';

describe('Button', () => {
it('calls onClick when clicked', async () => {
const handleClick = vi.fn();
render(<Button label="Click me" onClick={handleClick} />);

const btn = screen.getByRole('button', { name: /click me/i });
await userEvent.click(btn);

expect(handleClick).toHaveBeenCalledOnce();
});
});

Run npm test and you'll see:

✓ src/Button.test.tsx (1 test)
✓ Button › calls onClick when clicked (15ms)

Done. Vitest is live.

Key Environment Settings Worth Knowing

globals: false — If you prefer explicit imports (import { describe, it } from 'vitest'), set globals: false and add vitest to your ESLint imports config.

node environment — Use environment: 'node' for testing utilities, API mocks, and non-DOM logic. Node tests run 10x faster because they skip DOM simulation.

deps.optimizer — If you have slow third-party libraries, Vitest can pre-bundle them. See the Vitest config reference for test.deps.optimizer.

Key Takeaways

  • Vitest is 3–5x faster than Jest because it skips Node's CommonJS transpilation and uses Vite's ES module pipeline.
  • Install vitest, jsdom, and @vitejs/plugin-react in one command.
  • Create vitest.config.ts with globals: true and environment: 'jsdom' for React component tests.
  • Add a setup file to configure React Testing Library's cleanup hook.
  • Define test scripts in package.json for watch mode, UI mode, and coverage.
  • Verify setup by writing one simple component test and running npm test.

Frequently Asked Questions

Why use Vitest instead of Jest for React?

Vitest runs 3–5x faster than Jest on typical React codebases because it leverages Vite's native ES module pipeline instead of Node's CommonJS transpilation. For a codebase with 500+ tests, this means feedback in seconds rather than minutes. Vitest also has excellent React Testing Library integration out of the box.

Do I need jsdom, or can I use node environment?

Use jsdom for React component tests because components render into a DOM tree. Use node for utility functions, API mocking, and business logic that doesn't touch the DOM. You can use both in the same test suite by setting environment per-file with a comment: // @vitest-environment jsdom at the top of any file.

What if my Vite config already has aliases or plugins?

Vitest inherits your vite.config.ts by default, so aliases (like @/ for src/) and plugins (like React Fast Refresh) already work. If you want separate test config, name your file vitest.config.ts and Vitest will use only that—but that's rare; sharing the Vite config is better.

How do I add TypeScript support to my tests?

Vitest has zero-config TypeScript support out of the box. If your tsconfig.json has strict mode enabled, Vitest will check your tests the same way. No additional setup needed.

Can I run Vitest in CI (GitHub Actions, etc.)?

Yes. Add npm test -- --run to your CI pipeline (the --run flag exits after one pass instead of watch mode). Vitest also has built-in reporters for JUnit XML and JSON, useful for CI dashboards: vitest --reporter=junit --outputFile=test-results.xml.

Further Reading