Why you should start using projects in Vitest configuration

11 January 2026
#testing#vitest#vitest-browser-mode

I am a huge fan of Vitest. I use Jest in some projects (mostly for legacy reasons) but for all new projects Vitest is my default test runner.

I have to admit, I've always maintained multiple configuration files. I even wrote a blog post about how to set up Vitest Browser Mode and suggested using two different config files.

Thanks to a PR and feedback from someone on the Vitest team (Vladimir ) I am completely converted - Vitest projects are so much easier to maintain and they're so easy to set up too!

What are Vitest projects?

Within your main Vitest config, a project is a separate test runner configuration, that shares a base config but overrides config specific to that project.

(Your Vitest config can have multiple projects. Typically you'd use it to run different test files/directories with different configuration/environment.)

What you can use projects for in Vitest

If you have different sets of tests, then you can put each type in its own project. Then you can run just those tests.

For example:

  • split up your tests between faster unit and slower integration tests
  • split up your Vitest Browser-Mode tests and your regular (non Browser-mode tests)
  • separate frontend component tests from backend API tests
  • different projects that require different environments (e.g. node, happy-dom, jsdom)
  • separate projects that require different global setup/teardown logic

Why use projects instead of multiple config files?

  • Very easy to setup, just one file
  • Simple way to share duplicated config between different projects
  • They are very easy to run with --project your_project_name.
  • You can also run tests from multiple projects (like yarn vitest --project proj1 --project proj2) at the same time. Something that is not possible with multiple configs

How to set them up

Let's say you have two sets of tests - unit and integration tests.

In this blog post I will assume they either have the file ending *.unit.test.ts and *.integration.test.ts (or .tsx)

We will have two projects. Let's update our package.json

code
{
  "scripts": {
    "test:unit": "vitest --project unit",
    "test:integration": "vitest --project integration"
  }
}

And in your Vitest config file:

code
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    setupFiles: ['./vitest.setup.ts'],
    environment: 'jsdom',
    projects: [
      {
        extends: true,
        test: {
          name: 'unit', // optional, but it makes it easier to give each project a unique name
          include: [
            '**/*.unit.test.{ts,tsx}',
          ],
        },
      },
      {
        extends: true,
        test: {
          name: 'integration',
          include: [
            '**/*.integration.test.{ts,tsx}',
          ],
        },
      },
    ],
  },
});

And that is all you need to do.

More info on extends: true later on, but this just means that the project inherits the config from the top level test config

How it was done with separate vitest config files (Show details)

You might be reading this and wondering how you can do the same thing, but with two different vitest config files.

It is as simple as these two steps:

  • having two files (e.g. vitest.unit.config.ts and vitest.integration.config.ts)
  • then using the config file option when running vitest - like vitest --config=vitest.unit.config.ts

How to use Vitest projects with Vitest Browser Mode

It works in a very similar way - just pass in the browser config options to your browser project.

In the next example we set browser config, and have different setupFiles:

code
import { defineConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';

export default defineConfig({
  test: {
    globals: true,
    projects: [
      {
        extends: true,
        test: {
          name: 'unit',
          setupFiles: [
            './vitest.setup.ts',
          ],
          include: [
            '**/*.test.{ts,tsx,js,jsx}',
          ],
          environment: 'jsdom',
        },
      },

      // the project with browser mode config:
      {
        extends: true,
        test: {
          name: 'browser',
          setupFiles: [
            './vitest.browser.setup.ts',
          ],
          include: [
            './**/*.browser.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
          ],
          browser: {
            enabled: true,
            provider: playwright(),
            instances: [
              { browser: 'chromium' },
            ],
          },
        },
      },
    ],
  },
});

You can see a working version in this starter Vitest Browser Mode github repo

How to select different tests for your different projects

Option 1 - file based suffixes:

Using file based suffixes is the easiest way for most projects.

For example *.integration.test.tsx and *.unit.test.tsx

But if you already have all your tests with *.test.tsx, it isn't as straightforward.

You can however combine it with the exclude:

code
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'unit',
          include: [
            '**/*.test.{ts,tsx}',
          ],
          exclude: [
            '**/*.integration.test.{ts,tsx}',
          ], // << exclude *integration.test.ts here
        },
      },
      {
        test: {
          name: 'integration',
          include: [
            '**/*.integration.test.{ts,tsx}',
          ],
        },
      },
    ],
  },
});

Option 2 - different directories

Depending on your app and file structure, using different directories can also work and be very straightforward to set up.

A huge downside to this is that it is often beneficial to colocate your tests next to the implementation - otherwise it isn't easy to find and isn't clear if a test exists for a file if it is located in a directory somewhere else in the repo. In the examples on this page though we avoid that being a huge issue by always putting it in ./__tests__/unit/ or ./__tests__/integration/.

code
export default defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'unit',
          include: [
            'src/**/__tests__/unit/**/*.test.{ts,tsx}',
          ],
        },
      },
      {
        test: {
          name: 'integration',
          include: [
            'src/**/__tests__/integration/**/*.test.{ts,tsx}',
          ],
        },
      },
    ],
  },
});

How to reduce duplication between projects

In the examples on this page it always uses extends: true, which means any shared configuration between projects is put in the root config option.

In the following example, the globals and environment are used in both of the 2 projects, because of extends: true.

code
export default defineConfig({
  test: {
    globals: true, // << extends to each project
    environment: 'jsdom', // << extends to each project
    projects: [
      {
        extends: true, // << because of this
        test: {
          name: 'unit',
          include: [
            '**/*.unit.test.ts',
          ],
        },
      },
      {
        extends: true, // << because of this
        test: {
          name: 'integration',
          include: [
            '**/*.integration.test.ts',
          ],
        },
      },
    ],
  },
});

Should you use Vitest projects if you have only one project?

There is no practical advantage - you might as well wait until you need to add another project.

If you have a simple app, no need to overcomplicate it if there is no benefit.

What you cannot put into project configurations

You have to put coverage and reporters into the root config. If you need separate configuration for these, then you will have to use separate configuration files

How to run all projects

You can run all projects by just omitting any --project flag when calling the vitest command.

Examples of Vitest projects

Found this useful? Share this article

TwitterLinkedIn Facebook

🎯 Become the best at testing FE apps: Get exclusive testing tips & tricks delivered to your inbox

Stop wasting hours debugging flaky tests. Join hundreds of developers who get practical, battle-tested testing strategies straight to their inbox.

Every issue includes real-world code examples, Vitest best practices (including Vitest Browser Mode), testing patterns that actually work, and e2e testing strategies you can implement immediately.

✨ No fluff. Just actionable tips that make your tests better.

✅ Free forever✅ No spam✅ Unsubscribe anytime

Sent every 2-3 weeks. I respect your inbox and your time. Each email is packed with value, not filler.

Want to become a pro at testing your React apps?

I've got a huge range of interactive lessons from beginner to expert level.