Content Scripts
CRXJS provides content scripts with Vite HMR.
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.
IIFE content scripts
CRXJS normally runs content scripts through an async loader. That loader enables
Vite HMR and regular ESM imports, but it can be too late for document_start
scripts that must patch host page APIs before page code runs.
Use an IIFE content script for timing-sensitive work like intercepting fetch,
XMLHttpRequest, DOM events, or globals in the main world. CRXJS emits the
script as a standalone bundle with its imports inlined, and Chrome runs that
file directly.
IIFE content scripts do not receive in-place Vite HMR. During development CRXJS rebuilds the standalone file, but the updated script only runs after the host page reloads or navigates again.
Recommended architecture
Keep most of your extension logic in the default isolated world content script. It stays separated from page JavaScript, can use the extension APIs available to content scripts, and receives full Vite HMR in development.
Use a MAIN world IIFE only for the part that must run inside the host page
environment. Treat it as a small bridge: install the early hook at
document_start, then pass commands or data to the isolated content script with
window.postMessage, CustomEvent, or DOM events. This keeps the non-HMR script
small and stable.
Enable IIFE output
Add .iife before the source extension. This works for
.iife.ts, .iife.tsx, .iife.js, and .iife.jsx.
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.
Injected extension pages do have access to the full Chrome API, however.
If you load an HTML file from a content script, you need to declare the file as a web-accessible resource.
You will also need to add the HTML file to your Vite config under
build.rollupOptions.input.
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.
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().