Skip to main content

Overview

CRXJS parses your extension manifest to discover entry files and configure your build. The manifest is imported into your Vite config, so you can use JSON, JavaScript, or TypeScript.
Paths in the manifest are relative to your Vite project root, not to the manifest file location.

TypeScript Manifests

CRXJS exports a defineManifest helper function similar to Vite’s defineConfig. It provides autocompletion and supports dynamic or async manifest definitions.
manifest.config.ts
import { defineManifest } from '@crxjs/vite-plugin'
import packageJson from './package.json'
const { version } = packageJson

// Convert from Semver (example: 0.1.0-beta6)
const [major, minor, patch, label = '0'] = version
  // can only contain digits, dots, or dash
  .replace(/[^\d.-]+/g, '')
  // split into version parts
  .split(/[.-]/)

export default defineManifest(async (env) => ({
  manifest_version: 3,
  name:
    env.mode === 'staging'
      ? '[INTERNAL] My Extension'
      : 'My Extension',
  // up to four numbers separated by dots
  version: `${major}.${minor}.${patch}.${label}`,
  // semver is OK in "version_name"
  version_name: version,
}))
Chrome Extensions don’t use Semver. Read about the Chrome Extension version format.

Manifest Paths

All paths inside the manifest are relative to your Vite project root. Use paths that start with a letter or directory name.
{
  "options_page": "options.html",
  "devtools_page": "pages/devtools.html"
}
Don’t use relative (./) or absolute (/) path prefixes in manifest file paths.

How CRXJS Processes the Manifest

During the build, CRXJS:
  1. Parses the manifest to find all entry files (HTML pages, content scripts, background scripts)
  2. Emits these files as Rollup chunks for bundling
  3. Transforms file references to their final output names
  4. Validates that the manifest uses Manifest V3 (V2 is not supported)
From the source code (plugin-manifest.ts:311):
if (manifest.manifest_version !== 3)
  throw new Error(
    `CRXJS does not support Manifest v${manifest.manifest_version}, please use Manifest v3`
  )

JSON Schema Support

For JSON manifests, use a schema for autocompletion and validation. Configure VSCode:
settings.json
{
  "json.schemas": [
    {
      "fileMatch": ["manifest.json"],
      "url": "https://json.schemastore.org/chrome-manifest.json"
    }
  ]
}

Dynamic Manifests

The defineManifest function accepts an async function with environment info:
export default defineManifest(async (env) => {
  const isDev = env.mode === 'development'
  
  return {
    manifest_version: 3,
    name: isDev ? '[DEV] My Extension' : 'My Extension',
    permissions: isDev 
      ? ['storage', 'tabs', 'debugger'] 
      : ['storage', 'tabs'],
  }
})

Manifest Hooks

CRXJS provides plugin hooks for transforming the manifest:
  • transformCrxManifest - Runs after initial parsing, before file emission
  • renderCrxManifest - Runs after bundling, before final manifest output
These hooks enable CRXJS plugins to modify the manifest during the build process.

Pages

Learn about HTML pages in extensions

Background Scripts

Configure service workers

Content Scripts

Inject scripts into web pages

Web Accessible Resources

Expose extension resources

Build docs developers (and LLMs) love