## File: README.md
CRXJS
[📚 Documentation](https://crxjs.dev) |
[💬 Discord ](https://discord.com/invite/FnnE4XR7Wj)
## 📦 Create CRXJS Project
```shell
npm create crxjs@latest
```
> [!IMPORTANT]
> `@latest` MUST NOT be omitted, otherwise `npm` may resolve to a cached and outdated version of the package.
## ✨ Features
- 🧩 **Full Vite Plugin Ecosystem** - Leverage any Vite-compatible plugins with zero extra setup
- ⚙️ **Zero Configuration** - Start developing immediately with intelligent defaults
- 3️⃣ **Manifest V3 Support** - Built for modern Chrome extensions with enhanced security
- 🔥 **True Hot Module Replacement** - Instant UI updates while preserving extension state 🎈**works with content scripts**
- 📁 **Static Asset Import** - Directly reference images/fonts in your code
- 🤖 **Auto Web-Accessible Resources** - Automatic generation of `web_accessible_resources` manifest entries
> [!NOTE]
> Looking for MV2 support? See [`rollup-plugin`](packages/rollup-plugin/README.md)
## 💻 Development
- Clone this repository
- Install [pnpm](https://pnpm.io)
- Install dependencies using `pnpm install`
- Build the `vite-plugin` project using `pnpm build:vite-plugin`
- Playgrounds project is located at `playgrounds/**`, using `pnpm play` can run the playgrounds
- Cd into the `vite-plugin` directory using `cd packages/vite-plugin`
- Test using `pnpm run test`
- Use [DeepWiki](https://deepwiki.com/crxjs/chrome-extension-tools) to learn more about CRXJS
## 💝 Contributors
This project exists thanks to all the people who contribute.
And thank you to all our backers! 🙏
## 🤝 Supporting
If these plugins have helped you ship your product faster, please consider
[sponsoring me](https://github.com/sponsors/jacksteamdev) on GitHub.
---
## File: .changeset/README.md
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
---
## File: design/README.md
# Design Resources
You can find design files for CRXJS in this folder.
## Logo Design
Logo was made by
[Free Logo Maker: Design Custom Logos | Adobe Express](https://www.adobe.com/express/create/logo)
Fonts used: Lato-BlackItalic
Colors used: 298CD6,F2BAE4
Icon url: https://thenounproject.com/term/puzzle/8651
Icon by: [Daniel Heitz (@dnlhtz) / Twitter](https://twitter.com/dnlhtz)
## Pallets
By blue:
[ColorSpace - Color Palettes Generator and Color Gradient Tool](https://mycolor.space/?hex=%23298CD6&sub=1)
By pink:
[ColorSpace - Color Palettes Generator and Color Gradient Tool](https://mycolor.space/?hex=%23F2BAE4&sub=1)
---
## File: packages/vite-plugin-docs/docs/common/_get-url-for-images.mdx
import { ImageCodeBlock } from './ImageCodeBlock';
## Get the right URL
:::info
Content scripts share the origin of the page where they run.
:::
The browser treats the imported value `logo` as a URL from the host page. If the
content script is running on `https://google.com`, the `img` tag will
try to load from `https://google.com/logo.svg`.
Images first must be specified in the `web_accessible_resources` field in your `manifest.json` file:
```json title="manifest.json"
"web_accessible_resources": [
{
"resources": [ "icons/*.png"],
"matches": []
}
]
```
Then you reference the image in your content script using the [chrome.runtime.getURL](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/getURL) method:
---
## File: packages/vite-plugin-docs/docs/concepts/00-manifest.md
---
position: 0
title: Manifest JSON
id: manifest
---
# Extension Manifest
CRXJS parses the manifest to discover what entry files your extension uses.
Because we import the manifest into the Vite config, you can use JSON,
JavaScript, or TypeScript.
## TypeScript
CRXJS exports a helper function called `defineManifest`. It's similar to Vite's
`defineConfig` and supports autocompletion and dynamic or async definitions.
:::tip Did you know?
Chrome Extensions don't use Semver. Read more about the Chrome Extension version
format in the
[Google Developer Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/version/).
:::
The following example uses the version from `package.json` and dynamically sets
the name depending on Vite's mode.
```typescript title=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] CRXJS Power Tools'
: 'CRXJS Power Tools',
// up to four numbers separated by dots
version: `${major}.${minor}.${patch}.${label}`,
// semver is OK in "version_name"
version_name: version,
}))
```
## Manifest Paths
Paths inside the manifest are relative to the
[Vite project root](https://vitejs.dev/guide/#index-html-and-project-root), so
the location of the manifest file doesn't matter.
:::tip Use paths that start with a letter
```json title=manifest.json
{
"options_page": "options.html",
"devtools_page": "pages/devtools.html"
}
```
:::
:::danger Don't use relative or absolute paths
```json title=manifest.json
{
"options_page": "./options.html",
"devtools_page": "/root/user/.../devtools.html"
}
```
:::
## JSON Schema
If you're using a JSON file, consider using a schema like the one at
[JSON Schema Store](https://json.schemastore.org/chrome-manifest.json) to take
advantage of autocompletion and validation.
You can configure VSCode to use a JSON schema by adding this to your settings
file:
```json title=settings.json
{
"json.schemas": [
{
"fileMatch": ["manifest.json"],
"url": "https://json.schemastore.org/chrome-manifest.json"
}
]
}
```
---
## File: packages/vite-plugin-docs/docs/concepts/01-pages.md
---
position: 1
title: HTML Pages
---
# Extension Pages
CRXJS provides extension HTML pages with Vite HMR during development and
optimizations in a production build. The manifest can declare the most common
pages, such as the action popup and the options page, but not every extension
page has a place in the manifest.
## Extra HTML pages
If you need to declare extra HTML pages beyond those the manifest accommodates,
place them in the Vite config under `build.rollupOptions.input`.
This example includes a welcome page to open when the user installs the
extension.
```javascript title=vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
input: {
welcome: 'pages/welcome.html',
},
},
},
})
```
Vite will serve these input pages during development and include an optimized
version of them in the production build.
---
## File: packages/vite-plugin-docs/docs/concepts/02-background.md
---
position: 2
title: Background
---
# Extension Background
Chrome Extensions use a **service worker** to listen for Chrome API Events in
the background.
Add a service worker to your extension in the manifest under
`background.service_worker`. CRXJS uses module-type service workers because Vite
uses the ES module format.
```json title=manifest.json
{
"background": {
"service_worker": "src/background.ts",
"type": "module"
}
}
```
CRXJS loads the service worker from the Vite Dev Server during development, and
HMR causes a full extension reload when Vite detects a change to the background
code.
Learn more about extension service workers in the
[Chrome Developer Docs](https://developer.chrome.com/docs/extensions/mv3/service_workers/).
---
## File: packages/vite-plugin-docs/docs/concepts/03-content-scripts.md
---
position: 3
title: 'Content Scripts'
---
# Content Scripts
CRXJS provides content scripts with Vite HMR, so updates don't always require a
full host page reload. In addition, frameworks like React and Vue work in
content scripts the same as HTML pages.
:::tip Host Pages
The host page of a content script is the website where the content script is
running.
:::
## Static Assets
Feel free to import static assets! CRXJS automatically declares imported content
script dependencies as `web_accessible_resources` in the manifest.
## Use the extension URL
Content scripts share the origin of the host page, so convert imported static
assets to the extension origin using the Chrome API.
```javascript
import logo from './logo.png'
const url = chrome.runtime.getURL(logo)
```
## HTML in content scripts
It is possible to inject an extension page into a host page using an iframe. The
host page CSP does not affect the injected iframe even if the host page
specifies the `frame-src` policy.
An injected extension page loads inside a cross-origin iframe, so it does not
have access to the host page DOM like a content script.
```javascript title=content-script.js
const src = chrome.runtime.getURL('pages/iframe.html')
const iframe = new DOMParser().parseFromString(
``,
).body.firstElementChild
document.body.append(iframe)
```
Injected extension pages do have access to the full Chrome API, however.
:::info Configuration required
If you load an HTML file from a content script, you need to declare the file as
a web-accessible resource.
```json
{
"web_accessible_resources": [
{
"resources": ["pages/iframe.html"],
"matches": ["https://*.google.com/*"]
}
]
}
```
You will also need to add the HTML file to your Vite config under
`build.rollupOptions.input`.
```javascript title=vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
input: {
welcome: 'pages/iframe.html',
},
},
},
})
```
:::
### Imported HTML
If you need to render complex HTML in a content script without a framework, an
HTML file can serve as a static fragment by importing it as text using the
`?raw` query. This technique does not require the file to be web-accessible, and
you don't need to declare it in the Vite config.
```javascript
import html from './root.html?raw'
const iframe = new DOMParser().parseFromString(html).body.firstElementChild
iframe.src = chrome.runtime.getURL('pages/iframe.html')
document.body.append(iframe)
```
Importing an HTML file as text lets you take advantage of IDE language services
for HTML files. Depending on your HTML, this technique may be more concise than
using `document.createElement()`.
---
## File: packages/vite-plugin-docs/docs/getting-started/react/00-create-project.md
---
id: create-project
title: Create a project
tags:
- Getting started
- React
- Vite config
pagination_prev: null
slug: create-project
---
import {CreateProjectTabs} from '../\_create-project-tabs.mdx'
# Get Started with React
This quick guide will get you up and running with a Chrome Extension popup page.
You'll see how to integrate CRXJS with Vite, then explore Vite HMR in an
extension React HTML page. The first two sections take about 90 seconds!
:::tip package.json
Check `package.json` to ensure that `"type": "module"` is set. If this package
key is missing, Vite might not be able to build `vite.config.js`.
:::
## Install CRXJS
```sh
npm install --save-dev @crxjs/vite-plugin
```
## Create a web extension manifest
Create a file named `manifest.json` next to `vite.config.js`.
```json title=manifest.json
{
"manifest_version": 3,
"name": "CRXJS React Vite Example",
"version": "1.0.0",
"action": { "default_popup": "index.html" }
}
```
## Update the Vite config
Update `vite.config.js` to match the code below.
```js title=vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// highlight-start
import { crx } from '@crxjs/vite-plugin'
import manifest from './manifest.json'
// highlight-end
export default defineConfig({
plugins: [
react(),
// highlight-next-line
crx({ manifest }),
],
})
```
## First development build
Time to run the dev command. 🤞
```sh
npm run dev
```
That's it! CRXJS will do the rest.
Your project directory should look like this:
Next, we'll load the extension in the browser and give the development build a
test run.
---
## File: packages/vite-plugin-docs/docs/getting-started/react/01-dev-basics.md
---
id: dev-basics
title: Development basics
tags:
- HTML page
- Popup page
- React
- Vite config
slug: dev-basics
---
import Intro from '../\_dev-basics-intro.md';
import Installing from '../\_install-extension.md';
# Development Basics with React
## Install the extension
## Opening the extension
## Profit with Vite HMR
Once you've found the extension icon, right-click it and choose "Inspect popup
window". This will open the popup and the popup dev tools window. We need to
inspect the popup to keep it open while making changes.
And boom! HMR magic at work.