Using Vitest Workspaces to Test a Monorepo with Per‑Package Settings
Learn how to define Vitest workspaces, override options per package, and run all tests in parallel while avoiding common pitfalls.
02 Nov 2025, 22:06 UTC

The problem: testing multiple packages with different needs
When a monorepo contains several packages, some tests need a browser‑like DOM (jsdom) while others run fine in a plain Node environment. Keeping a separate Vitest config for each package leads to duplicated settings, and running tests one‑by‑one loses the speed benefits of Vitest’s built‑in thread pool.
Thesis: a single Vitest workspace config lets you keep shared defaults while allowing per‑package overrides
By declaring a workspaces array in the root vitest.config.ts, Vitest treats each matching config as a workspace. All workspaces inherit the root configuration but can override individual options in their own config file. Running vitest from the repository root then executes every workspace in parallel, showing a consolidated output with a prefix for each package.
Setting up the workspace
- Make sure you have Vitest ≥ 0.30.0 installed (check with
vitest --version). - At the repository root create or edit
vitest.config.ts:
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// shared defaults for all packages
globals: true,
environment: 'node',
},
workspaces: [
'packages/**/vitest.{ts,tsx}',
],
})
The workspaces field uses a glob pattern that points to any vitest.{ts,tsx} file inside the packages directory. Run this command from the repository root (no special permissions required):
$ vitestIf a package does not yet have its own config file, Vitest will fall back to the root config – a point we return to in the limitation section.
Per‑package overrides
Create a config for a package that needs jsdom:
// packages/pkg-a/vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { environment: 'jsdom', }, })For a package that is fine with the default Node environment you can either omit the config file or keep an empty one:
// packages/pkg-b/vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({})Add a simple test to each package to verify the environment:
// packages/pkg-a/test/example.test.ts import { test, expect } from 'vitest' test('uses jsdom environment', () => { expect(typeof document).toBe('object') })// packages/pkg-b/test/example.test.ts import { test, expect } from 'vitest' test('runs in Node environment', () => { expect(typeof document).toBe('undefined') })Running tests and interpreting output
From the repository root execute:
$ vitestYou should see output similar to:
[pkg-a] ✓ uses jsdom environment (12ms) [pkg-b] ✓ runs in Node environment (9ms) Test Files 2 passed (2)The prefixes
[pkg-a]and[pkg-b]indicate which workspace produced each result. Vitest runs the two suites in parallel using its internal thread pool; total time is roughly the longest single suite rather than the sum.To run a single pass and exit use the
--runflag:$ vitest --runTo keep the test process alive and re‑run on changes, add
--watch:$ vitest --watchBoth flags work with the workspace setup and respect the
--reporteroption if you prefer a different output format.Trade‑off and limitation
The workspace feature requires Vitest 0.30.0 or newer. Older versions silently ignore the
workspacesfield and treat the configuration as a single project, which can hide misconfigurations.Another practical limitation appears when a workspace lacks its own
vitest.config.ts. Vitest will fall back to the root config, meaning that a package expecting jsdom will incorrectly run in Node if you forget to add its config file. This can lead to false‑negative tests.How to verify the setup is working as intended:
- Check the Vitest version:
vitest --versionshould return 0.30.0 or higher. - Look for the workspace prefixes in the output; missing prefixes indicate that Vitest is not recognizing the workspaces.
- Deliberately remove
packages/pkg-a/vitest.config.tsand run the tests again – the test that expectsdocumentshould now fail, confirming the fallback behavior.
Actionable closing
Adding a workspace to your Vitest configuration gives you a single source of truth for shared test settings while still allowing each package to tailor its environment. Start by defining the workspaces glob in the root config, add per‑package config files where needed, and run vitest from the repo root to see the parallel execution with clear prefixes. Keep an eye on the Vitest version and ensure every package that needs a custom environment ships its own config file to avoid silent fallbacks. With these steps you can efficiently test a monorepo without sacrificing flexibility or speed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.