Testing is often skipped in the early stages of a project, but once components grow and logic gets complicated, having a test suite becomes essential. Setting up Vitest with TypeScript in a Vite-powered project is supposed to be easy. However, some combinations of dependencies, configuration files, and tooling quirks make the process feel more confusing than it should be. The goal here is to create a clean, working setup for unit and component tests using Vitest, TypeScript, and Vite, while avoiding common configuration errors that slow things down.
Install Required Packages
Start by installing the packages necessary for running tests. Vitest comes with most of what you need, but TypeScript support, mocking, and React testing may need additional packages.
Use this command:
npm install -D vitest @vitest/ui @vitest/coverage-v8 jsdom happy-dom
If the project uses React, include testing libraries too:
npm install -D @testing-library/react @testing-library/jest-dom
This setup allows for testing React components in a simulated browser-like environment using jsdom
or happy-dom
. The difference between them depends on performance and compatibility. For most projects, jsdom
is the safer choice.
Configure vite.config.ts
Make sure the Vite config file is using the defineConfig
helper. Add a test
block in the configuration to define how Vitest should behave.
Here’s a complete example:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/setupTests.ts',
include: ['src/**/*.{test,spec}.{ts,tsx}'],
},
})
A few important points here:
globals: true
allows usingdescribe
,it
, andexpect
without importing them manually.environment: 'jsdom'
simulates a DOM, which is important for UI tests.setupFiles
is optional but useful if you’re adding global test utilities likejest-dom
.
Create Setup File
If you are using React Testing Library or adding global matchers, a setup file is helpful. Create src/setupTests.ts
and include:
import '@testing-library/jest-dom'
This makes matchers like toBeInTheDocument()
available in your tests.
Update tsconfig.json
TypeScript sometimes needs help resolving Vitest’s types. In the tsconfig.json
, make sure the types
array includes vitest
:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"jsx": "react-jsx",
"types": ["vitest"]
},
"include": ["src"]
}
This avoids errors like “Cannot find name ‘describe'” or “Cannot find name ‘expect'” when running or writing tests.
Write a Sample Test
Having a working test early confirms everything is set up correctly. Create a new file, for example src/components/Button.test.tsx
:
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import Button from './Button'
describe('Button', () => {
it('renders with correct label', () => {
render(<Button label="Click Me" />)
expect(screen.getByText('Click Me')).toBeInTheDocument()
})
})
This basic test checks that a button with the expected label is rendered. It’s a great starting point before diving into more complex logic.
Use UI Mode (Optional)
Vitest includes a UI mode that makes debugging easier. Run the following command:
npx vitest --ui
This opens a web interface where you can run and re-run tests visually, see errors and logs, and view test coverage if enabled.
Add Scripts to package.json
It helps to have shortcuts for running tests. Add these scripts:
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:watch": "vitest --watch"
}
This allows running tests with npm run test
or opening the UI with npm run test:ui
.
File Structure Example
Below is a sample project structure that keeps everything organized:
Folder / File | Purpose |
---|---|
vite.config.ts | Vite and Vitest configuration |
tsconfig.json | TypeScript configuration |
src/setupTests.ts | Global test setup |
src/components/ | React components |
src/components/*.test.tsx | Component test files |
Use with Coverage
For code coverage, add this to the test
section of vite.config.ts
:
coverage: {
reporter: ['text', 'html'],
}
Then run:
npx vitest run --coverage
This gives a terminal summary and a full HTML report of which lines were tested.
Common Errors
Here are some common mistakes and how to fix them:
- No
defineConfig
error – Usually happens if you’re using an old version of Vite orvite.config.ts
is incorrectly written. Make sure the file is valid and Vite is up to date. - DOM errors in non-jsdom environment – Confirm that the
environment
is set tojsdom
. - Test file not found – Ensure test filenames end with
.test.ts
,.spec.ts
,.test.tsx
, etc., and match the pattern invite.config.ts
.
Conclusion
Setting up Vitest with TypeScript in a Vite project doesn’t have to be difficult, but it does require careful alignment between tools. By configuring Vite properly, aligning the TypeScript setup, and writing a test early, the entire process becomes easier to manage. This structure works well for growing projects, especially when adding new features that depend on components behaving predictably. Once the environment is in place, writing useful unit tests becomes much faster and far more reliable — and there’s no better time to add that test than right after fixing a bug or finishing a new feature.