Configuration
Vitest reads configuration from vitest.config.ts or vite.config.ts. It shares the same config format as Vite.
Basic Setup
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// test options
},
})Using with Existing Vite Config
Add Vitest types reference and use the test property:
// vite.config.ts
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
},
})Merging Configs
If you have separate config files, use mergeConfig:
// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(viteConfig, defineConfig({
test: {
environment: 'jsdom',
},
}))Common Options
defineConfig({
test: {
// Enable global APIs (describe, it, expect) without imports
globals: true,
// Test environment: 'node', 'jsdom', 'happy-dom'
environment: 'node',
// Setup files to run before each test file
setupFiles: ['./tests/setup.ts'],
// Include patterns for test files
include: ['**/*.{test,spec}.{js,ts,jsx,tsx}'],
// Exclude patterns
exclude: ['**/node_modules/**', '**/dist/**'],
// Limit test discovery to a directory (faster than broad excludes)
dir: './src',
// Test timeout in ms
testTimeout: 5000,
// Hook timeout in ms
hookTimeout: 10000,
// Coverage configuration (v4+: define `include`, no more `all`)
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'html'],
include: ['src/**/*.ts'],
},
// Run each file in an isolated module graph (threads/forks pools only)
isolate: true,
// Pool: 'forks' (default), 'threads', 'vmForks', 'vmThreads'
pool: 'forks',
// v4+: pool options are top-level (poolOptions was removed)
maxWorkers: 4,
fileParallelism: true,
// Clear mock call history before each test (v5: defaults to true)
clearMocks: true,
// Restore spies created with vi.spyOn between tests
restoreMocks: true,
// Retry failed tests
retry: 0,
// Stop after first failure
bail: 0,
// v5: repeat every test N times regardless of result (flaky-hunting)
repeats: 0,
},
})v5 Performance Config
defineConfig({
test: {
// Persist transformed modules on disk, reused across reruns AND separate
// processes (stable in v5; was experimental.fsModuleCache). Cache lives in
// node_modules by default; clear with `vitest --clearCache`. Not for browser.
fsModuleCache: true,
// Inline projects that don't change the Vite config reuse the declaring
// config's Vite server (default true) — shared files transformed once.
// Set false if a plugin must be re-instantiated per project.
sharedViteServer: true,
// Inject CJS globals (module/exports/require/__filename/__dirname) into
// every module (default true). Set false for stricter ESM-like scope.
injectCjsGlobals: false,
// Detect async resources leaking past a test file (slow; debugging only)
detectAsyncLeaks: false,
},
})v4/v5 Config Changes
- v5 requires Vite >= 6.4.0 and Node >= 22.12.0.
viteis now a required peer dependency (Yarn users mustyarn add -D vite). clearMocksdefaults totruein v5 —vi.clearAllMocks()runs before every test (history cleared, implementations kept). SetclearMocks: falsefor the old behavior.- Inline projects inherit the root config by default (
extendsdefaults totrue) — including Viteplugins/resolve.alias. Setextends: falseto opt out. See advanced-projects. testNamePattern(-t) matches the>-joined full name ('math > adds'), not the space-joined Jest form.vi.mock/vi.unmock/vi.hoistedmust be at the top level — nested calls now throw (they were only warned before).- Pool default is
forks(child processes), notthreads. poolOptionsremoved —maxThreads/maxForksare now top-levelmaxWorkers;singleThread/singleForkbecomemaxWorkers: 1, isolate: false; VMmemoryLimitisvmMemoryLimit.minWorkerswas removed.workspaceremoved — useprojects.vitest.workspace.tsno longer supported.coverage.allandcoverage.extensionsremoved — by default only covered files are reported; setcoverage.includeexplicitly.- Simplified
exclude— onlynode_modules/.gitexcluded by default. Usetest.dirto scope discovery, or spreadconfigDefaults.exclude. - Config not looked up from parent dirs — pass
--configexplicitly when running from a subdirectory. .vitestartifact dir — blob reports (.vitest/blob/), attachments (.vitest/attachments/), and thehtml/json/junitreporters now all live under a single.vitest/directory; add one entry to.gitignore.test.sequential/describe.sequential/sequentialremoved — use{ concurrent: false }.deps.optimizer.webrenamed todeps.optimizer.client;deps.inline/deps.externalmoved underserver.deps.
Conditional Configuration
Use mode or process.env.VITEST for test-specific config:
export default defineConfig(({ mode }) => ({
plugins: mode === 'test' ? [] : [myPlugin()],
test: {
// test options
},
}))Projects (Monorepos)
Run different configurations in the same Vitest process:
defineConfig({
test: {
projects: [
'packages/*',
{
test: {
name: 'unit',
include: ['tests/unit/**/*.test.ts'],
environment: 'node',
},
},
{
test: {
name: 'integration',
include: ['tests/integration/**/*.test.ts'],
environment: 'jsdom',
},
},
],
},
})Key Points
- Vitest uses Vite's transformation pipeline - same
resolve.alias, plugins work vitest.config.tstakes priority overvite.config.ts- Use
--configflag to specify a custom config path (required from subdirectories in v5) process.env.VITESTis set totruewhen running tests- Test config uses
testproperty, rest is Vite config - v5 requires Vite >= 6.4.0 and Node >= 22.12.0;
viteis a required peer dependency