Content Scripts
Content scripts run in the context of web pages in an isolated world. This allows multiple content scripts from various extensions to coexist without conflicting with each other's execution and to stay isolated from the page's JavaScript.
Use cases:
- Scraping data from the current web page
- Selecting, finding, and styling elements from the current web page
- Injecting UI elements into the current web page
- Injecting code into the "main world" context
Adding a single content script
Since Plasmo's default Typescript configuration treats all source files as
modules, if you don't have any imports or exports in your code, you'll have to
add an export {}
line at the start of your file. (You will see this warning
when creating your first content script!)
Create a content.ts
file that exports an empty object and hack away!
export {}
console.log(
"You may find that having is not so pleasing a thing as wanting. This is not logical, but it is often true."
)
Reload your extension, open a web page, then open its inspector:
See with-content-script (opens in a new tab) for a full example.
Adding multiple content scripts
Create a contents
directory for multiple content scripts, and add your content scripts there. Make sure their names describe what they do!
See with-many-content-scripts (opens in a new tab) for an example.
Config
Sometimes, you'll want to run a content script on certain pages. You can provide a custom content script configuration by exporting a config object from your content script:
import type { PlasmoContentScript } from "plasmo"
export const config: PlasmoContentScript = {
matches: ["<all_urls>"],
all_frames: true
}
Working with this configuration object is a breeze thanks to the exported PlasmoContentScript
type 🥳.
To learn more about the config and each property, check out Chrome's official documentation (opens in a new tab).
Injecting into the main world
You must inject code into the main world if you'd like to modify the window
object from your content script. It's not currently possible to declaratively inject content scripts.
Instead, Chrome offers a chrome.scripting.executeScript
API that lets you inject content scripts into the main world. First, add the scripting permission in your package.json's 'manifest.permissions' array:
{
...
"manifest" : {
"permissions": ["scripting"]
}
}
Then, inject your content script into the main world by calling chrome.scripting.executeScript
from your background service worker:
chrome.scripting.executeScript(
{
target: {
tabId // the tab you want to inject into
},
world: "MAIN", // MAIN to access the window object
func: windowChanger // function to inject
},
() => {
console.log("Background script got callback after injection")
}
)
For the func
key, you can pass in a Typescript function from your project, which will automatically convert to a JavaScript function when your extension bundles.
If you have imports in your injected code, they won't automatically be bundled in with it.
See with-main-world-content-script-injection (opens in a new tab) for an example.
Fetching external API and CORS
Because content scripts run within the context of a web page, they are subject to the same-origin policy. To mitigate CORS restrictions, you can use the Plasmo Messaging API to proxy the request via your background service worker.
Importing resources
To import external assets into your content script, you can use the url:
scheme:
import myFile from "url:./path/to/my/file/something.js"
The url:
scheme will automatically resolve the something.js
asset and add it to the web_accessible_resources
declaration in the built bundle. The above myFile
variable will be a string containing the URL to the asset:
> console.log(myFile)
chrome-extension://<your chrome ext id>/something.eb20bc99.js?1656000646313
Alternatively, you can use the data-base64
or the data-text
scheme to import and embed the asset directly into your code. For small assets, these schemes should work well.
Please see this note about ~
import
resolution