READ-ONLY PACKAGE PREVIEW

vitest/references/advanced-projects.md

Version e53a142a2420 · MIT. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.

← Return to resource and package checksum


name: projects-workspaces description: Multi-project configuration for monorepos and different test types


Projects

Run different test configurations in the same Vitest process.

v5 Config Inheritance & Nested Projects

  • Inline projects inherit the root config by default — extends now defaults to true, so root Vite options (plugins, resolve.alias) and test options are inherited. Arrays like setupFiles are appended, not replaced. Opt out with extends: false, or inherit from another file with extends: './vitest.shared.ts'. Projects referenced as config files/directories still don't inherit the root.
  • Referenced config files can declare their own projects — such a config acts as a container providing nested projects named app (unit), app (e2e), etc. In v4 a referenced config's projects field was silently ignored, so audit merged configs that pull one in.
  • Inline projects share the declaring config's Vite server by default (sharedViteServer) — the declaring config runs once, so plugin config hooks no longer run per project. A project gets its own server only when it changes the Vite config (plugins, alias, css, deps.optimizer, root, browser, mode). Set sharedViteServer: false if a plugin must be re-instantiated per project.
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()], // inherited by every inline project (v4 needed extends: true)
  test: {
    projects: [
      { test: { name: 'unit', include: ['**/*.unit.test.ts'] } },
      // a package that declares its own projects becomes a nested container:
      './packages/app/vitest.config.ts', // -> "app (unit)", "app (e2e)", ...
    ],
  },
})

Basic Projects Setup

// vitest.config.ts
defineConfig({
  test: {
    projects: [
      // Glob patterns for config files
      'packages/*',

      // Inline config
      {
        test: {
          name: 'unit',
          include: ['tests/unit/**/*.test.ts'],
          environment: 'node',
        },
      },
      {
        test: {
          name: 'integration',
          include: ['tests/integration/**/*.test.ts'],
          environment: 'jsdom',
        },
      },
    ],
  },
})

Monorepo Pattern

defineConfig({
  test: {
    projects: [
      // Each package has its own vitest.config.ts
      'packages/core',
      'packages/cli',
      'packages/utils',
    ],
  },
})

Package config:

// packages/core/vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    name: 'core',
    include: ['src/**/*.test.ts'],
    environment: 'node',
  },
})

Different Environments

Run same tests in different environments:

defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'happy-dom',
          root: './shared-tests',
          environment: 'happy-dom',
          setupFiles: ['./setup.happy-dom.ts'],
        },
      },
      {
        test: {
          name: 'node',
          root: './shared-tests',
          environment: 'node',
          setupFiles: ['./setup.node.ts'],
        },
      },
    ],
  },
})

Browser + Node Projects

defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'unit',
          include: ['tests/unit/**/*.test.ts'],
          environment: 'node',
        },
      },
      {
        test: {
          name: 'browser',
          include: ['tests/browser/**/*.test.ts'],
          browser: {
            enabled: true,
            name: 'chromium',
            provider: 'playwright',
          },
        },
      },
    ],
  },
})

Shared Configuration

// vitest.shared.ts
export const sharedConfig = {
  testTimeout: 10000,
  setupFiles: ['./tests/setup.ts'],
}

// vitest.config.ts
import { sharedConfig } from './vitest.shared'

defineConfig({
  test: {
    projects: [
      {
        test: {
          ...sharedConfig,
          name: 'unit',
          include: ['tests/unit/**/*.test.ts'],
        },
      },
      {
        test: {
          ...sharedConfig,
          name: 'e2e',
          include: ['tests/e2e/**/*.test.ts'],
        },
      },
    ],
  },
})

Project-Specific Dependencies

Each project can have different dependencies inlined:

defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'project-a',
          server: {
            deps: {
              inline: ['package-a'],
            },
          },
        },
      },
    ],
  },
})

Running Specific Projects

# Run specific project (v5 adds the -p shorthand)
vitest --project unit
vitest -p integration

# Multiple projects / wildcards
vitest --project unit --project e2e
vitest --project="packages*"

# Exclude a project
vitest --project="!browser"

# Nested projects: --project matches the prefix
vitest -p app                 # every project of the "app" config
vitest -p "app (unit)"        # just one nested project

Providing Values to Projects

Share values from config to tests:

// vitest.config.ts
defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'staging',
          provide: {
            apiUrl: 'https://staging.api.com',
            debug: true,
          },
        },
      },
      {
        test: {
          name: 'production',
          provide: {
            apiUrl: 'https://api.com',
            debug: false,
          },
        },
      },
    ],
  },
})

// In tests, use inject
import { inject } from 'vitest'

test('uses correct api', () => {
  const url = inject('apiUrl')
  expect(url).toContain('api.com')
})

With Fixtures

const test = base.extend({
  apiUrl: ['/default', { injected: true }],
})

test('uses injected url', ({ apiUrl }) => {
  // apiUrl comes from project's provide config
})

Per-Project Pool & Isolation (v4)

Since the v4 pool rework, isolation, parallelism, and Node CLI options can be set per project:

defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'unit',
          isolate: false,                 // fast, non-isolated unit tests
          exclude: ['**/*.integration.test.ts'],
        },
      },
      {
        test: {
          name: 'sequential',
          include: ['**/*.sequential.test.ts'],
          fileParallelism: false,         // run these files one at a time
        },
      },
      {
        test: {
          name: 'staging',
          execArgv: ['--env-file=.env.staging'], // per-project Node flags
        },
      },
    ],
  },
})

Global Setup per Project

defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'with-db',
          globalSetup: ['./tests/db-setup.ts'],
        },
      },
    ],
  },
})

Key Points

  • Projects run in same Vitest process (replaces the removed workspace option)
  • Each project can have different environment, pool, isolation, and config
  • Use glob patterns for monorepo packages
  • Run specific projects with --project (supports wildcards and ! exclusion)
  • Use provide to inject config values into tests
  • Inline projects inherit root config by default (v5 extends: true); set extends: false to opt out
  • Referenced configs that declare projects provide nested projects (name (child))