Vitest is faster than Jest because it leverages native ES Modules and eschews the need for a transpilation step for most modern JavaScript codebases.

Here’s Vitest in action, running tests for a simple React component:

// src/Counter.jsx
import React, { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
// src/Counter.test.jsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';

describe('Counter', () => {
  it('should render the initial count and increment on button click', () => {
    render(<Counter />);
    expect(screen.getByText('Count: 0')).toBeInTheDocument();

    const button = screen.getByText('Increment');
    fireEvent.click(button);

    expect(screen.getByText('Count: 1')).toBeInTheDocument();
  });
});

To run these tests with Vitest, assuming you have it installed and configured (typically via a vitest.config.js file, though often not strictly necessary for basic setups):

  1. Install necessary packages:

    npm install -D vitest @vitejs/plugin-react jsdom
    # or
    yarn add -D vitest @vitejs/plugin-react jsdom
    
  2. Configure Vitest (optional for simple React setups, but good practice): Create vitest.config.js:

    import { defineConfig } from 'vitest/config';
    import react from '@vitejs/plugin-react';
    
    export default defineConfig({
      plugins: [react()],
      test: {
        environment: 'jsdom',
        setupFiles: './setupTests.js', // Optional: for global setup like @testing-library/jest-dom
      },
    });
    

    If you’re using @testing-library/jest-dom for assertions: Create setupTests.js:

    import '@testing-library/jest-dom';
    
  3. Add a test script to your package.json:

    {
      "scripts": {
        "test": "vitest"
      }
    }
    
  4. Run the tests:

    npm test
    # or
    yarn test
    

You’ll see output indicating the tests are running and passing.

The Mental Model: Speed Through Modernity

Jest, for a long time, was the de facto standard for JavaScript testing. Its strength lay in its comprehensive feature set and its ability to work with various JavaScript environments by abstracting away the underlying execution. It achieved this by transforming your code (transpilation) using tools like Babel, bundling it, and then running it in a Node.js environment. This process, while robust, introduces overhead. Every test file, every dependency, had to go through this transformation pipeline.

Vitest, on the other hand, is built on top of Vite. Vite’s core innovation is its development server, which leverages native ES Modules (ESM) in the browser. Instead of bundling your entire application for development, Vite serves your source code directly to the browser, leveraging the browser’s native ESM support. When a file is requested, Vite only needs to transpile it on demand, leading to near-instantaneous server start times and hot module replacement (HMR).

Vitest inherits this speed. When you run vitest, it doesn’t bundle your entire test suite upfront. Instead, it uses Vite’s pre-bundling and transformation capabilities. For most modern JavaScript projects that already use ES Modules, this means Vitest can execute your tests with minimal to no transpilation overhead. It directly imports and runs your test files as native ESM. The jsdom environment provides a browser-like DOM, and @vitejs/plugin-react (or similar plugins for other frameworks) handles framework-specific transformations. The result is a significantly faster test execution time, especially for larger codebases or when running tests frequently.

The core difference boils down to when and how code is processed. Jest processes all code upfront, bundling and transpiling everything before running any tests. Vitest, leveraging Vite’s architecture, processes code on demand, only transforming what’s necessary and running tests in a more native ESM environment. This "lazy" processing, combined with ESM’s efficiency, is the primary driver of Vitest’s speed advantage.

The Control Levers

  1. environment: This setting in vitest.config.js dictates the execution context. 'jsdom' is common for React/Vue/Svelte apps, mimicking a browser. 'node' is for backend or pure Node.js testing. Switching environments can impact performance and available APIs.
  2. plugins: Just like in Vite for application building, Vitest uses plugins. @vitejs/plugin-react is crucial for React projects, handling JSX transformation. For Vue, it’s @vitejs/plugin-vue. These plugins ensure framework-specific code is correctly processed.
  3. deps.inline: Sometimes, Vitest might struggle with pre-bundling certain dependencies. If you encounter issues or slow startup, you can force Vitest to inline specific dependencies (meaning they won’t be pre-bundled). For example, deps: { inline: ['your-library'] } tells Vitest to treat your-library as source code and process it on demand. This can be a performance trade-off: less pre-bundling means more on-demand processing, but it can resolve compatibility issues and sometimes improve startup for specific dependency graphs.
  4. cache.dir: Vitest caches pre-bundled dependencies to speed up subsequent runs. You can configure this directory. If you suspect cache corruption or want to force a full re-run, deleting this directory (or changing its path) will achieve that. The default is .vitest in your project root.

The one thing that most people don’t realize about Vitest’s speed is how much it benefits from the surrounding Vite ecosystem and its specific handling of modern JavaScript features. It’s not just about being "new"; it’s about deeply integrating with how modern browsers and Node.js execute code natively. When Vitest encounters a test file written with native ES Modules, it can often load and execute it directly without a significant transpilation step, unlike Jest, which typically requires Babel for all code. This direct execution path, combined with Vite’s efficient on-demand compilation for any necessary transformations (like JSX), dramatically reduces the overhead per test file.

The next step in optimizing your testing workflow will likely involve exploring parallel test execution and advanced code coverage reporting within Vitest.

Want structured learning?

Take the full Jest course →