crxjs
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 trigg
Security Assessment
About crxjs
Supports Chrome extension development with CRXJS, a tool that provides true hot module replacement for popups, options pages, content scripts, and side panels. Under the hood it is a Vite plugin (@crxjs/vite-plugin) that reads the manifest to auto-generate the extension output, handles content script injection, and manages the service worker build. It applies when setting up a new CRXJS project with any framework (React, Vue, Svelte, Solid, or Vanilla) and when extending, fixing HMR in, or configuring content scripts on an existing CRXJS project. For general Chrome extension architecture such as messaging, CSP, storage, and permissions, it points to a separate chrome-extension skill.
Getting started uses npm create crxjs@latest to scaffold a project interactively, or npm install @crxjs/vite-plugin -D to add it to an existing Vite project. The skill shows per-framework vite.config setups that add crx({ manifest }) alongside the framework plugin, noting that @vitejs/plugin-react gives the best HMR compatibility and how to cast the manifest if SWC is used. As an alternative to a static JSON manifest, defineManifest builds a type-safe dynamic manifest that can pull values from package.json and vary by command, and a triple-slash client reference enables types for the ?script and ?script&module import suffixes.
HMR behavior is documented per context: popup, options, and side panel get full state-preserving HMR, manifest and dynamic content scripts get true module-level HMR via an injected loader, service worker changes trigger an automatic full extension reload, and main-world scripts are skipped. Dynamic content scripts injected through chrome.scripting.executeScript use the ?script suffix (or ?script&module for the main world) to obtain a resolved path. Plugin options include browser selection and contentScripts settings like injectCss and hmrTimeout. The development workflow loads the unpacked dist directory once and then updates live, and the production build outputs dist for zipping and Chrome Web Store upload, with guidance to disable module preload. Known issues and workarounds cover Tailwind class HMR in content scripts, WebSocket port mismatch errors, and manifest version warnings.
FAQ
What build tool does CRXJS use?
CRXJS is a Vite plugin, published as @crxjs/vite-plugin. It reads the manifest to auto-generate the extension output, handles content script injection, and manages the service worker build.
Which frameworks are supported?
It works with any framework, including React, Vue, Svelte, Solid, and Vanilla TypeScript. The skill shows per-framework vite.config setups that add crx({ manifest }) alongside the framework's own Vite plugin.
How does HMR work for content scripts?
Content scripts get true module-level HMR because CRXJS generates a loader script that imports an HMR preamble, the HMR client, and your actual script, enabling real HMR without a full page reload. This works for both manifest-declared and dynamically imported (?script) content scripts.
How do I inject a content script programmatically?
Import it with the ?script suffix to get a resolved path and pass it to chrome.scripting.executeScript via the files option. For main-world injection without HMR, use the ?script&module suffix and set world to MAIN.
What should I do about WebSocket connection errors like ws://localhost:undefined/?
Those errors are caused by a port mismatch between the dev server and HMR config. The fix is to explicitly set both to the same value, for example server.port with strictPort plus hmr.port at the same number.
Install crxjs
Quick Setup:
- Copy the skill folder to
.claude/skills/ - Claude will automatically detect and use the skill
Repository
samber/cc-skills