К основному содержанию
скилл CX Codex

crxjs — скилл для Claude Code: HMR для Chrome-расширений

Скилл для разработки Chrome-расширений на CRXJS: подсказывает, как настроить настоящий HMR для popup, content scripts и side panel. Пригодится, если расширение уже собирается через Vite.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add samber/cc-skills --skill crxjs -a claude-code
Открыть на GitHub

Что делает crxjs

Скилл описывает работу с @crxjs/vite-plugin — плагином Vite, который читает манифест и сам собирает выход расширения. Агент получает готовые конфиги для React, Vue, Svelte и Vanilla TypeScript, а также шаблон defineManifest для типизированного манифеста с динамическими значениями вроде версии из package.json.

Отдельная тема — HMR по контекстам. Popup, options и side panel перезагружаются через WebSocket с сохранением состояния. Content scripts получают настоящий модульный HMR: CRXJS подставляет loader, преамбулу и клиент. Скрипты в main world HMR не получают — их loader пропускает, поэтому правки в них придётся проверять перезагрузкой страницы.

Для динамической инъекции есть суффиксы импорта ?script и ?script&module. Первый даёт путь для chrome.scripting.executeScript, второй — для инъекции в main world. Чтобы типы для этих импортов подхватились, в проект добавляют ссылку на @crxjs/vite-plugin/client. Скилл требует установленных git и node.

Какие задачи решает

  • настроить HMR для content script без перезагрузки страницы
  • собрать типизированный манифест с версией из package.json
  • подключить CRXJS к существующему проекту на Vite
  • инжектить content script программно через executeScript
  • разобраться, почему правки в service worker требуют перезагрузки расширения

Примеры запросов агенту

  • Настрой CRXJS в моём Vite-проекте на React, нужен HMR для popup и content script
  • Переведи манифест на defineManifest, версию бери из package.json
  • Добавь динамическую инъекцию content script через ?script в background

Как установить crxjs

1Через Skills CLI (Vercel)

npx skills add samber/cc-skills --skill crxjs -a claude-code

2Вручную: положить файлы

~/.claude/skills/crxjs/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/crxjs/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о crxjs

Как установить crxjs в Claude?

Выполните npx skills add samber/cc-skills --skill crxjs -a claude-code. Скилл окажется в ~/.claude/skills/crxjs/SKILL.md.

Что делает скилл crxjs?

Помогает агенту настраивать и дорабатывать Chrome-расширения на @crxjs/vite-plugin: конфиги Vite под разные фреймворки, defineManifest, HMR для content scripts и динамические импорты ?script.

Что нужно для работы скилла?

Установленные git и node. Сам проект должен собираться через Vite — CRXJS подключается как плагин.

Исходный текст

SKILL.md · MIT
---
name: crxjs
description: "CRXJS Chrome extension development — true HMR for popup, options, content scripts, side panels, manifest-driven builds, dynamic content script imports (`?script`, `?script&module`), and `defineManifest` for type-safe manifests. Uses Vite as its build tool. Use when the user mentions CRXJS, crxjs, @crxjs/vite-plugin, 'extension with hot reload', 'HMR for chrome extension', or wants to set up a CRXJS-based Chrome extension project with any framework (React, Vue, Svelte, Solid, Vanilla). Also trigger when the user has an existing CRXJS project and wants to add features, fix HMR issues, or configure content scripts with CRXJS. For general Chrome extension architecture (messaging, CSP, storage, permissions) -> See `samber/cc-skills@chrome-extension` skill."
user-invocable: true
license: MIT
compatibility: Designed for Claude Code, Codex or similar harness. Requires git, node.
metadata:
  author: samber
  version: "1.1.0"
  openclaw:
    emoji: "📝"
    homepage: https://github.com/samber/cc-skills
    requires:
      bins:
        - git
        - node
        - npm
    skill-library-version: "2.4.0"
allowed-tools: Read Edit Write Glob Grep Bash(git:*) Bash(gh:*) Bash(npm:*)
---

# CRXJS

CRXJS is a Chrome extension development tool that provides true HMR for popup, options, content scripts, and side panels. It reads your manifest to auto-generate the extension output, handles content script injection, and manages the service worker build. Under the hood it is a Vite plugin (`@crxjs/vite-plugin`).

## Current status

- **Package**: `@crxjs/vite-plugin` (v2.x stable, latest v2.4.0 as of March 2026)
- **Scaffolding**: `npm create crxjs@latest` (always use `@latest`)
- **Maintained by**: @Toumash and @FliPPeDround (since mid-2025)
- **GitHub**: github.com/crxjs/chrome-extension-tools (~4k stars)
- **Vite compatibility**: v3 through v8-beta

## Quick start

```bash
# Scaffold new project (picks framework interactively)
npm create crxjs@latest

# Or add to existing Vite project
npm install @crxjs/vite-plugin -D
```

## Vite config by framework

CRXJS is added as a Vite plugin. The setup varies slightly per framework.

### React

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [react(), crx({ manifest })],
});
```

Use `@vitejs/plugin-react` (not `plugin-react-swc`) for best HMR compatibility. If you must use SWC, cast the manifest:

```typescript
import { ManifestV3Export } from "@crxjs/vite-plugin";
const manifest = manifestJson as ManifestV3Export;
```

### Vue

```typescript
import vue from "@vitejs/plugin-vue";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [vue(), crx({ manifest })],
});
```

### Svelte

```typescript
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [svelte(), crx({ manifest })],
});
```

### Vanilla TypeScript

```typescript
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.json";

export default defineConfig({
  plugins: [crx({ manifest })],
});
```

## defineManifest — type-safe dynamic manifest

Instead of a static JSON file, use CRXJS's `defineManifest` for dynamic values and full TypeScript autocompletion:

```typescript
// manifest.ts
import { defineManifest } from "@crxjs/vite-plugin";
import pkg from "./package.json";

export default defineManifest((config) => ({
  manifest_version: 3,
  name: config.command === "serve" ? `[DEV] ${pkg.name}` : pkg.name,
  version: pkg.version,
  description: pkg.description,
  permissions: ["storage", "activeTab", "scripting"],
  action: {
    default_popup: "src/popup/index.html",
    default_icon: {
      "16": "public/icons/icon16.png",
      "48": "public/icons/icon48.png",
    },
  },
  background: {
    service_worker: "src/background/index.ts",
    type: "module",
  },
  content_scripts: [
    {
      matches: ["https://*/*"],
      js: ["src/content/index.ts"],
      css: ["src/content/styles.css"],
    },
  ],
  options_page: "src/options/index.html",
  side_panel: { default_path: "src/sidepanel/index.html" },
  icons: {
    "16": "public/icons/icon16.png",
    "48": "public/icons/icon48.png",
    "128": "public/icons/icon128.png",
  },
}));
```

Import in vite.config.ts:

```typescript
import manifest from "./manifest";
// ... crx({ manifest })
```

## Type declarations

Add to a `src/vite-env.d.ts` or `src/crxjs.d.ts`:

```typescript
/// <reference types="@crxjs/vite-plugin/client" />
```

This enables types for `?script` and `?script&module` imports.

## HMR behavior by context

| Context | HMR | How it works |
| --- | --- | --- |
| Popup | Full HMR | WebSocket-based, state preserved |
| Options page | Full HMR | Same as popup |
| Side panel | Full HMR | Same as popup |
| Content script (manifest) | True HMR | CRXJS injects loader + HMR client |
| Content script (dynamic) | True HMR | Via `?script` import |
| Service worker | Auto-reload | Changes trigger full extension reload |
| Main world scripts | No HMR | Skipped by CRXJS loader |

Content script HMR works because CRXJS generates a loader script that imports an HMR preamble, the HMR client, and your actual script — enabling real module-level HMR without full page reload. This is CRXJS's main differentiator.

## Dynamic content script imports

For content scripts injected programmatically (not in manifest), CRXJS provides special import suffixes:

```typescript
// background.ts — ?script gives you a resolved path for executeScript
import contentScript from "./content?script";

chrome.action.onClicked.addListener(async (tab) => {
  await chrome.scripting.executeScript({
    target: { tabId: tab.id! },
    files: [contentScript],
  });
});
```

For main world injection (no HMR):

```typescript
import mainWorldScript from "./inject?script&module";

await chrome.scripting.executeScript({
  target: { tabId },
  world: "MAIN",
  files: [mainWorldScript],
});
```

## CRXJS plugin options

```typescript
crx({
  manifest,
  browser: "chrome", // 'chrome' | 'firefox'
  contentScripts: {
    injectCss: true, // auto-inject CSS for content scripts
    hmrTimeout: 5000, // HMR connection timeout (ms)
  },
});
```

## Development workflow

```bash
# Start dev server (outputs to dist/ with HMR)
npm run dev

# 1. Open chrome://extensions
# 2. Enable "Developer mode"
# 3. Click "Load unpacked"
# 4. Select the dist/ directory
# 5. Edit code — popup/content scripts update instantly via HMR
# 6. Service worker changes trigger automatic extension reload
```

After loading once, subsequent `npm run dev` sessions reconnect automatically. No need to re-load the extension unless manifest.json changes.

## Production build

```bash
npm run build    # outputs to dist/
```

The dist/ directory is ready to zip and upload to Chrome Web Store:

```bash
cd dist && zip -r ../extension.zip .
```

Disable Vite's module preload to avoid CWS rejection of inline scripts:

```typescript
build: {
  modulePreload: false;
}
```

## Known issues and workarounds

### Tailwind CSS HMR in content scripts

New Tailwind classes may not trigger CSS updates in content scripts. **Workaround**: restart dev server after adding new utility classes. Improved in v2.4.0 but not fully resolved. Ensure `injectCss: true` in config.

### WebSocket connection errors (`ws://localhost:undefined/`)

**Cause**: port mismatch between dev server and HMR config. **Fix**: explicitly set both to the same value:

```typescript
server: {
  port: 5173,
  strictPort: true,
  hmr: { port: 5173 },
}
```

### "Manifest version 2 is deprecated" warning

If you see this, your manifest is being interpreted as MV2. **Fix**: ensure `"manifest_version": 3` is set.

### Content scripts not injecting on file:// URLs

Chrome requires the user to enable "Allow access to file URLs" in the extension settings at chrome://extensions. CRXJS cannot change this.

### HMR stops working after Chrome update

CRXJS's HMR relies on injecting a content script that connects to the dev server's WebSocket. Chrome security updates occasionally break this. **Fix**: update to the latest CRXJS version, which tracks Chrome changes.

## CRXJS vs alternatives

| Feature | CRXJS | WXT | Plasmo |
| --- | --- | --- | --- |
| Content script HMR | True HMR | File-based reload | Partial |
| Framework support | Any Vite framework | Any | React-focused |
| Abstraction level | Thin (Vite plugin) | Full framework | Full framework |
| Messaging helpers | None (use chrome.\* directly) | Built-in | Built-in |
| Storage wrappers | None | Built-in | Built-in |
| Cross-browser | Chrome + Firefox | Chrome + Firefox + Safari | Chrome + Firefox |
| File-based routing | No | Yes | Yes |
| Learning curve | Low (know Vite, know CRXJS) | Medium | Medium |

**Choose CRXJS when**: you want minimal abstraction over raw Chrome APIs and value content script HMR above all. CRXJS stays out of the way — no magic routing, no wrapper APIs, just your code with HMR.

**Choose WXT when**: you want conventions, built-in utilities, and cross-browser support.

**Choose Plasmo when**: you're React-focused and want the highest-level abstraction.

## Project structure (recommended)

```
my-extension/
├── src/
│   ├── background/
│   │   └── index.ts
│   ├── content/
│   │   ├── index.ts
│   │   └── styles.css
│   ├── popup/
│   │   ├── index.html        <- CRXJS resolves HTML entry points
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── options/
│   │   ├── index.html
│   │   └── main.tsx
│   ├── sidepanel/
│   │   ├── index.html
│   │   └── main.tsx
│   └── shared/
│       ├── messages.ts
│       └── storage.ts
├── public/
│   └── icons/
├── manifest.ts               <- or manifest.json
├── vite.config.ts
├── tsconfig.json
└── package.json
```

CRXJS resolves HTML files referenced in the manifest automatically. Your popup.html can use standard `<script type="module" src="./main.tsx">` and it works.

If you encounter a bug or unexpected behavior in CRXJS, open an issue at github.com/crxjs/chrome-extension-tools/issues.

© samber · MIT · samber/cc-skills

Похожие скиллы

Все скиллы по теме «Браузер и парсинг» →
chrome-extension-development Mindrally Ведёт агента по сборке расширений Chrome на Manifest V3: манифест, service worker, content scripts, попап, публикация в Chrome Web Store. CX Codex +1 2 800установок chrome-extension pproenca Скилл с 67 правилами по Chrome Extensions на Manifest V3. Пригодится, когда пишете service worker, content script или мигрируете с MV2. CX Codex +1 717установок chrome-extension-dev-js-typescript-cursorrules-pro PatrickJS Правило для Cursor, которое настраивает агента писать код расширений Chrome на JavaScript и TypeScript по единому своду соглашений. ★ 41 011звёзд репо vite-patterns affaan-m Скилл подсказывает, как настраивать Vite, плагины, HMR, переменные окружения и сборку. Нужен при работе с vite.config.ts и Vite-проектами. CX Codex +1 4 800установок clerk-chrome-extension-patterns clerk Набор инструкций по интеграции @clerk/chrome-extension в браузерные расширения. Помогает настроить вход в popup, сессии в service worker и делегирование OAuth через веб-приложение. CX Codex +1 14 400установок chrome-extensions GoogleChrome Помогает создавать и публиковать расширения для Chrome по стандартам Manifest V3. Задаёт правила для фоновых скриптов, боковых панелей и разрешений. CX Codex +1 10 900установок

Из того же набора19

site-launch-checklist Пошагово готовит веб-сайт к релизу через серию проверок: от DNS, TLS и CSP-заголовков до SEO, аналитики и соответствия GDPR. Пригодится перед выкаткой проекта в прод. CX Codex +1 4 400установок copywriting-prose-creator Создаёт PROSE.md с правилами лексики, синтаксиса и ритма бренда. Нужен для единого стиля текстов у авторов и ИИ. CX Codex +1 4 500установок promql-cli Скилл учит агента гонять PromQL через CLI promql-cli: instant- и range-запросы, поиск метрик, вывод таблицей, CSV, JSON или графиком. CX Codex +1 4 400установок substack-ghostwriting Пишет и адаптирует выпуски рассылок и статьи для Substack с подбором авторского тона. Помогает тестировать идеи через Notes и оформлять посты под конверсию в подписчиков. CX Codex +1 4 400установок press-release-writer Пишет пресс-релизы для запусков, раундов инвестиций, партнёрств и кризисных коммуникаций. Подстраивает формат под тип медиа и регион. CX Codex +1 4 400установок training-report Собирает структурированный отчёт о тренинге или воркшопе через интервью и экспортирует черновик Markdown в итоговый документ .docx. CX Codex +1 4 200установок skill-progressive-disclosure-design Помогает решить, что оставить в SKILL.md, а что вынести в references/, чтобы скилл не съедал контекст зря. CX Codex +1 4 300установок humanizer-en-asd-ste100 Переписывает технический английский по ASD-STE100 и убирает следы ИИ-генерации. Нужен для инструкций, процедур и API-документации. CX Codex +1 2 600установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой