Skip to main content

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.

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.

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.

content-script.js
const src = chrome.runtime.getURL('pages/iframe.html')

const iframe = new DOMParser().parseFromString(
`<iframe class="crx" src="${src}"></iframe>`,
).body.firstElementChild

document.body.append(iframe)

Injected extension pages do have access to the full Chrome API, however.

Configuration required

If you load an HTML file from a content script, you need to declare the file as a web-accessible resource.

{
"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.

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.

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().