A glance at Vitest
September 09, 2025

I’ve recently had the chance to work on front-end testing for a short time, and I wanted to write this note for anyone who’s new to Vitest, curious about the current state of front-end testing, or just checking out my writing skills (please do not roast me!).
A Quick Introduction
We have heard about Jest for a while—it's widely used, backed by Meta, and is the default option when you first initialize a React project.
But now we have Vitest. What is it, why should we use it, and how do we set it up?
Why Vitest?
Once Upon a Time
Before browsers could support ES Modules natively, bundlers like Webpack with Babel or other tools were needed to transform ES Modules (ESM) into a single CommonJS format file. This approach provided cleaner code, correct import sequences, and fewer network requests, making it executable in older browsers.
For example, when you write this code:
Babel jumps in and transforms it to CommonJS. Here's what it does:
From TSX:
- Remove types
- Transpile JSX
- Transpile ESM
To CommonJS: (so older browser versions that don't support ESM can execute it)
It transforms to:
At that time, when the Meta team built ReactJS, they needed a test runner. Following this pattern, the team built Jest, which takes CommonJS as input for testing.
And Then...
Browsers now support ES Modules directly. When you import anything, the browser understands it, picks up the file, and if that file contains any import statements, it can import them dynamically without any concerns.
With Vite, since browsers support ES Modules directly, Vite takes advantage of this and doesn't need to compile to CommonJS. It serves ESM directly, eliminating the bundler step and enabling faster development builds. You can understand the flow as:
TSX
- Remove types
- Transform JSX
To ESM: (which modern browsers that support ES Modules can execute)
The problem now is that if you use Vite with Jest, you need two different compilation strategies:
- Development: TSX → (esbuild) → ESM
- Test: TSX → (Babel) → CommonJS
You have two different pipelines for the same job—why not use just one? That's why we have Vitest - Vite-native test runner. Vitest can understand ESM directly, so we have consistent behavior. And yes, Vitest can understand both ESM and CommonJS, so Vitest can be used even with non-Vite projects.
Vitest is the default test option when you initialize a project with Vite, but what if you're not using Vite?
Setting Up Vitest Without Vite
Vitest
Let's add our main character: Vitest.
Then, create a vitest.config.ts file in the root of your project:
Since we are testing React, we need to add a plugin for Vitest to understand React components:
Then, add the plugin to your vitest.config.ts file:
Now, let's modify our package.json file. To get beautiful coverage reports, let's add @vitest/coverage-v8 to our dev dependencies:
And add a script to your package.json file:
At this point, you can run your tests with yarn test. However, if you initialize the project from the beginning, when you run yarn test with the initial project, it will fail with the default test. The error looks like this:
Reference: https://stackoverflow.com/questions/77611978/invalid-chai-property-in-vitest
If you are seeing this, we're missing something (else you can skip RTL section below or checking what is going on with others)...
React Testing Library
Let's add @testing-library/react and @testing-library/jest-dom to our project (if you don't have them yet, but you should have them if you initialize the project from the beginning):
And @testing-library/jest-dom:
Let's put it inside our setup file setupTests.ts:
Now, let's create the setup file and enhance it a bit more:
This is for cleanup after each test.
And let's add it to our vitest.config.ts file:
That's all for now
Now when you run yarn test, the result will be seen at terminal as (for example):
Features
Like other test runners, Vitest supplies so many matchers that support the assertion. But let's talk about other features too:
UI mode
This is pretty cool feature, let's see:
On the left hand is the test suite, with the icon of result. The main part is the Module Graph, where you can see the dependency graph of the testing files. Ignoring node_modules supports cleaner view.
If you want to try, add --ui flag at the end of your test command:
It will automatically opens a tab for you, but if not, it's here:
Watch mode
Vitest turn on this mode by default, and only triggers the related test files instead of everything, which are affected by the changed, or the file you are running and the related (as your imports) specifically, and excludes the rest. This deliver faster and more focus develop experience.
Small note, the affected one is opposite with the one at Module Graph, that one is what the component needs, this one are what needs the component.
Test coverage
This is the basic feature that all test runner should have, I do agree. With Vitest, instead of logging out all of the test results, even the unrelated files, they only displays the one which is run at watch mode.
I give you the sneak peak at the implementation, I added --coverage to the script above:
In-source testing
Reading at the docs here, I tried, you can just take a quick look:
They include the test into the code file, without extracting to seperated test file. This can be good, when you share the same closure, but my insight is this only applicable for the util, helper which is pure logic, no complicated effects, dom, ... But this is a new way we can consider for writing test.
And so many more to be added.
That's all! Have fun with testing!
Conclusion
I have a good testing experience with Vitest, easy to config, clean coverage output, fast and focus execution, easy to debug my test, so, putting my trust in Vitest, for now.
Reference
Table of Contents