# Understanding the manifest.ts File in CRXJS: Purpose, Structure, and Type Safety

> Explore the CRXJS manifest.ts file for strongly-typed Manifest V3 schemas. Get compile-time validation and IDE autocomplete for your Chrome extension manifests.

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

---

**The [`manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/manifest.ts) file in CRXJS provides a strongly-typed TypeScript schema for Chrome Extension Manifest V3, enabling compile-time validation and IDE autocomplete for extension manifests across the entire toolchain.**

The `crxjs/chrome-extension-tools` repository uses this central type definition file to power its Vite and Rollup plugins. Located at [`packages/vite-plugin/src/node/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/manifest.ts), it exports the `ManifestV3` interface and related helper types that allow developers to write their extension configuration in TypeScript rather than raw JSON, catching errors before runtime.

## Purpose of the manifest.ts File in CRXJS

The primary purpose of [`manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/manifest.ts) is to provide an **exhaustive, strongly-typed representation** of the Chrome (and Firefox) extension manifest schema. According to the CRXJS source code, this file serves three critical functions:

- **Compile-time validation** – Developers writing [`manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/manifest.ts) files get immediate TypeScript errors for misspelled keys or incorrect value types (e.g., passing a number where the schema expects a string).
- **IDE intelligence** – The interfaces provide autocomplete for Chrome-specific keys like `chrome_url_overrides`, `declarative_net_request`, and `web_accessible_resources`.
- **Tooling consistency** – Both the Vite plugin and Rollup plugin import these same definitions from [`packages/vite-plugin/src/node/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/manifest.ts), ensuring manifests are interpreted identically across the entire CRXJS toolchain.

## Structure of the manifest.ts Type Definitions

The file organizes type definitions into logical sections that mirror the official Chrome extension manifest schema while accounting for cross-browser compatibility.

### Utility Types for Complex Manifest Fields

The file begins with small, reusable interfaces for nested manifest structures. These handle complex fields that appear multiple times throughout the schema.

```typescript
export interface DeclarativeNetRequestResource {
  id: string
  enabled: boolean
  path: string
}

export interface WebAccessibleResourceByMatch {
  matches: string[]
  resources: string[]
  use_dynamic_url?: boolean
}

```

These utility types ensure that `web_accessible_resources` and `declarative_net_request` rules maintain consistent structure across different extension configurations.

### Browser-Specific Background Script Types

CRXJS handles the divergence between Chrome and Firefox background script implementations through distinct interfaces combined into a union type.

- **`ChromeManifestBackground`** – Defines the Chrome-specific `service_worker` field with optional `type: 'module'` for ES module support.
- **`FirefoxManifestBackground`** – Defines Firefox's `scripts` array and `persistent` boolean flag.

These are exported as a union type `ChromeManifestBackground | FirefoxManifestBackground` assigned to the `background` property in the main manifest interface.

### The ManifestV3 Interface

The centerpiece of the file is the massive `ManifestV3` interface that implements the complete Manifest V3 specification. It organizes fields by requirement level:

```typescript
export interface ManifestV3 {
  // Required fields
  manifest_version: number
  name: string
  version: string

  // Recommended fields
  default_locale?: string
  description?: string
  icons?: chrome.runtime.ManifestIcons

  // Optional Chrome-specific sections
  action?: chrome.runtime.ManifestAction
  background?: ChromeManifestBackground | FirefoxManifestBackground
  content_scripts?: chrome.runtime.ManifestContentScript[]
  permissions?: chrome.runtime.ManifestPermissions[]
  host_permissions?: string[]
  // ... additional optional fields
}

```

The interface references Chrome's official type definitions (e.g., `chrome.runtime.ManifestAction`) while extending them with CRXJS-specific browser compatibility layers.

### Firefox Gecko Permissions

For Firefox-specific extensions, the file includes `GeckoPermissionsRequired` and `GeckoPermissionsOptional` interfaces. These type the `browser_specific_settings.gecko.data_collection_permissions` field, allowing developers to declare Firefox-specific permissions while maintaining type safety.

## How CRXJS Uses manifest.ts for Type Checking

The Vite plugin and Rollup plugin consume these type definitions to validate user configurations at build time. When a developer writes their manifest in TypeScript (typically as [`src/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/src/manifest.ts)), the plugins import the `ManifestV3` interface to enforce schema compliance.

For example, the fixture at [`packages/rollup-plugin/__fixtures__/extensions/mv3-manifest-ts-single-input/src/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/rollup-plugin/__fixtures__/extensions/mv3-manifest-ts-single-input/src/manifest.ts) demonstrates real-world usage, importing the interface and exporting a fully typed manifest object. This approach prevents runtime manifest errors that would otherwise only surface during extension loading in the browser.

## Practical Examples: Writing Type-Safe Extension Manifests

### Defining a Standard ManifestV3 Configuration

```typescript
// src/manifest.ts
import { ManifestV3 } from '@crxjs/vite-plugin';

const manifest: ManifestV3 = {
  manifest_version: 3,
  name: 'My Awesome Extension',
  version: '1.0.0',
  action: {
    default_popup: 'popup.html',
  },
  background: {
    service_worker: 'service_worker.ts',
    type: 'module',
  },
  content_scripts: [
    {
      matches: ['https://*/*', 'http://*/*'],
      js: ['content.ts'],
      run_at: 'document_idle',
    },
  ],
  permissions: ['storage', 'tabs'],
  host_permissions: ['*://*.example.com/*'],
};

export default manifest;

```

The TypeScript compiler immediately flags invalid keys (such as `permissoins` instead of `permissions`) or incorrect types (such as passing a number for the `version` field).

### Configuring Firefox-Specific Background Scripts

```typescript
import { ManifestV3 } from '@crxjs/vite-plugin';

const manifest: ManifestV3 = {
  manifest_version: 3,
  name: 'Cross-Browser Demo',
  version: '0.1',
  background: {
    scripts: ['background.js'],
    persistent: false,
  },
};

```

Because `background` accepts the union type `ChromeManifestBackground | FirefoxManifestBackground`, TypeScript enforces that only Firefox-compatible properties (`scripts`, `persistent`) are present when targeting that browser.

### Declaring Web Accessible Resources with Dynamic URLs

```typescript
import { ManifestV3 } from '@crxjs/vite-plugin';

const manifest: ManifestV3 = {
  manifest_version: 3,
  name: 'Dynamic Assets',
  version: '1.0',
  web_accessible_resources: [
    {
      matches: ['*://*/*'],
      resources: ['images/*.png'],
      use_dynamic_url: true,
    },
  ],
};

```

The `WebAccessibleResourceByMatch` interface ensures the `use_dynamic_url` property is correctly typed as an optional boolean, preventing accidental string assignments.

## Summary

- The [`manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/manifest.ts) file at [`packages/vite-plugin/src/node/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/manifest.ts) serves as the central TypeScript schema for Chrome Extension Manifest V3 within CRXJS.
- It exports the `ManifestV3` interface along with browser-specific types like `ChromeManifestBackground` and `FirefoxManifestBackground` to handle cross-platform differences.
- The file enables compile-time validation and IDE autocomplete for extension developers using the Vite or Rollup plugins.
- Utility interfaces such as `DeclarativeNetRequestResource` and `WebAccessibleResourceByMatch` provide granular type safety for complex manifest fields.
- Real-world usage examples in the repository demonstrate how importing these types prevents runtime manifest errors.

## Frequently Asked Questions

### Where is the manifest.ts file located in the CRXJS repository?

The main type definitions reside at [`packages/vite-plugin/src/node/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/vite-plugin/src/node/manifest.ts). This file exports all manifest-related interfaces used throughout the CRXJS toolchain, including the primary `ManifestV3` interface consumed by both the Vite and Rollup plugins.

### What is the difference between ChromeManifestBackground and FirefoxManifestBackground?

`ChromeManifestBackground` defines the Chrome-specific structure using `service_worker` and optional `type: 'module'` for ES modules, while `FirefoxManifestBackground` uses the `scripts` array and `persistent` boolean required by Firefox's Manifest V3 implementation. The `ManifestV3` interface accepts these as a union type for the `background` property, ensuring only valid properties for the target browser are allowed.

### Can I use manifest.ts with the Rollup plugin or only Vite?

Both plugins support TypeScript manifest files. The Rollup plugin fixture at [`packages/rollup-plugin/__fixtures__/extensions/mv3-manifest-ts-single-input/src/manifest.ts`](https://github.com/crxjs/chrome-extension-tools/blob/main/packages/rollup-plugin/__fixtures__/extensions/mv3-manifest-ts-single-input/src/manifest.ts) demonstrates importing `ManifestV3` from `@crxjs/vite-plugin` and using it to type-check a Rollup-based extension build.

### Does the ManifestV3 interface support Manifest V2 fields?

No, the `ManifestV3` interface specifically targets Chrome Extension Manifest V3. It omits V2-specific fields like `browser_action` and `page_action` (replaced by the unified `action` field in V3) and enforces `manifest_version: 3`. Developers targeting Manifest V2 would need to use different type definitions or extend the interfaces manually.