# How CRXJS Vite Plugin Performs Manifest Version Validation During Build

> Learn how the CRXJS vite plugin validates manifest version 3 during build. Ensures compliance and prevents errors early in development for Chrome extensions.

- Repository: [crxjs/chrome-extension-tools](https://github.com/crxjs/chrome-extension-tools)
- Tags: how-to-guide
- Published: 2026-02-28

---

**The CRXJS Vite plugin validates the manifest version synchronously in the `config` hook by checking that `manifest.manifest_version` equals `3`, immediately throwing an error if any other version is detected.**

The CRXJS Vite plugin enforces strict Manifest V3 compliance for Chrome extensions by validating the manifest version before any build steps execute. This validation occurs in the `config` hook of the plugin, ensuring developers receive immediate feedback if they attempt to use deprecated Manifest V2 formats. Understanding this validation mechanism helps prevent build failures and ensures compatibility with modern Chrome extension standards.

## Where Manifest Version Validation Occurs

The validation logic resides in [`packages/vite-plugin/src/node/plugin-manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/plugin-manifest.ts) inside the `crx:manifest-init` plugin. According to the CRXJS source code, the plugin registers a `config` hook—the earliest Vite lifecycle hook available to plugins—to perform this check.

When Vite initializes, the plugin calls `getOptions(config)` to retrieve the user-provided manifest object or manifest factory function. This acquisition happens synchronously during Vite's configuration resolution phase, ensuring the build halts before any file processing begins if validation fails.

## The Validation Implementation

The validation follows a strict three-step process:

1. **Manifest acquisition** – The plugin retrieves the manifest via `getOptions(config)`, which handles both static objects and dynamic functions.
2. **Version comparison** – It reads `manifest.manifest_version` and compares it against the supported integer `3`.
3. **Error throwing** – If the version is not exactly `3`, the plugin throws an error with the message:  

   ```

   CRXJS does not support Manifest vX, please use Manifest v3
   ```

This check runs synchronously, meaning Vite never proceeds to the build phase with an unsupported manifest. The source code includes a comment `// TODO: build this out into full validation plugin`, indicating that while comprehensive schema validation may arrive in future versions, the version gate serves as the critical safeguard today.

## Code Examples

### Valid Manifest V3 Configuration

This configuration passes validation and allows the build to proceed:

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: {
        manifest_version: 3,          // ✅ Required version
        name: 'My Extension',
        version: '1.0.0',
        action: {
          default_popup: 'index.html',
        },
      },
    }),
  ],
});

```

### Invalid Manifest V2 Error Handling

Attempting to use Manifest V2 triggers an immediate build failure:

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: {
        manifest_version: 2,          // ❌ Unsupported version
        name: 'Old Extension',
        version: '0.1.0',
        browser_action: {
          default_popup: 'index.html',
        },
      },
    }),
  ],
});

```

```bash
$ vite build
Error: CRXJS does not support Manifest v2, please use Manifest v3
    at config (packages/vite-plugin/src/node/plugin-manifest.ts:...)

```

### Dynamic Manifest Functions

The validation also works when providing a function that generates the manifest at runtime:

```typescript
// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: (env) => ({
        manifest_version: env.mode === 'production' ? 3 : 3, // Must return 3
        name: 'Dynamic Manifest',
        version: '1.0.0',
      }),
    }),
  ],
});

```

Even with dynamic generation, the `config` hook executes the function and validates the returned object's `manifest_version` before Vite continues.

## Why Early Validation Matters

Performing manifest version validation in the `config` hook provides three critical benefits:

- **Safety** – Prevents accidental bundling of MV2 extensions, which are deprecated and unsupported by the CRXJS toolchain.
- **Early feedback** – Developers see the error immediately when running `vite` or `vite build`, rather than discovering issues during browser loading or distribution packaging.
- **Future-proofing** – The synchronous check establishes a foundation for the TODO-commented "full validation plugin" that may add schema validation for permissions, host permissions, and other Manifest V3 requirements.

## Summary

- CRXJS validates manifest version in the `config` hook located in [`packages/vite-plugin/src/node/plugin-manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/plugin-manifest.ts) before Vite processes any source files.
- Only `manifest_version: 3` is supported; any other integer value triggers an immediate fatal error.
- The validation uses `getOptions(config)` to retrieve the manifest, then checks `manifest.manifest_version` against the hardcoded value `3`.
- Error messages explicitly state: "CRXJS does not support Manifest vX, please use Manifest v3".
- Dynamic manifest functions are executed and validated during the config resolution phase, ensuring runtime-generated manifests still comply with V3 requirements.

## Frequently Asked Questions

### What happens if I use Manifest V2 with CRXJS?

The build fails immediately during Vite's configuration resolution phase. The plugin throws an error stating "CRXJS does not support Manifest v2, please use Manifest v3" and exits without processing any source files.

### Can I use a function to generate the manifest dynamically?

Yes, CRXJS supports passing a function to the `manifest` option in the plugin configuration. The function receives the Vite environment object and must return a manifest object with `manifest_version: 3`. The validation occurs after the function executes but before the build begins.

### Where is the manifest version validation code located?

The validation logic is implemented in the `config` hook inside [`packages/vite-plugin/src/node/plugin-manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/plugin-manifest.ts). This file imports the manifest via `getOptions` from [`packages/vite-plugin/src/node/plugin-optionsProvider.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/plugin-optionsProvider.ts) and performs the version check against the `ManifestV3` type definition found in [`packages/vite-plugin/src/node/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/manifest.ts).

### Is Manifest V3 validation the only check performed?

Currently, only the `manifest_version` integer is strictly validated. However, the source code contains a TODO comment indicating plans to expand the [`plugin-manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/plugin-manifest.ts) logic into a comprehensive validation plugin that may verify permissions, host permissions, and other Manifest V3 schema requirements in future releases.