Hello everyone 👋
If you are looking at this repository, it’s probably because at some point you tried to load remote scripts into your Chrome extension.
With the arrival of Manifest V3, however, this functionality has been blocked: now it’s mandatory to include all scripts directly inside the extension files.
And let’s be honest, this is super annoying — especially if you distribute the extension in unpacked format (so, outside the Chrome Web Store).
That’s why I decided to find a workaround.
This repository is a small example of how to bypass this limitation. I’m convinced this approach can be extended much further, and its potential is really high.
-
Download the repository to your computer.
-
Load the extension in Chrome: go to
chrome://extensions/
, enable developer mode, and load thechrome_extension
folder as an unpacked extension. -
Make the
remote_files
folder accessible remotely:This folder contains the scripts that the extension will dynamically load. They must be served over HTTP, they cannot just sit locally.
Some practical examples:
- Next.js: copy the
remote_files
folder insidepublic/
. This way the files will be accessible at a URL like:http://localhost:3000/remote_files/content/script.json
- Express: serve the folder by adding to your Express server:
This way, the files will be available at:
app.use("/remote_files", express.static("remote_files"));
http://localhost:3000/remote_files/content/script.json
- Any other static server: just expose the
remote_files
folder as a public directory (Apache, Nginx, Vercel, etc.).
In short, you need to make sure that visiting a URL like:
http://YOUR-DOMAIN/remote_files/content/script.json
correctly serves the file.
- Next.js: copy the
-
Configure the domain in the service worker: if you’re not using
http://localhost:3000
, openchrome_extension/service_worker/background.js
and edit the constant:const domain = "http://localhost:3000";
replacing it with the domain you are using.
-
Done ✅
At this point the extension is ready, and you can manage it by loading remote scripts directly from theremote_files
folder.
Normally, if we try to load remote scripts in a MV3 extension, Chrome blocks them.
My idea was: why not convert the JavaScript scripts into another form and then interpret them directly in the extension?
The format I chose is the JavaScript AST (Abstract Syntax Tree).
In short: an AST is a structured representation of code, like a tree that describes each instruction (variables, functions, calls, etc.).
Inside the extension I included a small interpreter that takes this AST (in JSON format), reads it, and executes it.
The interpreter you’ll find in this repo (chrome_extension/modules/interpreter.js
) is quite simple and can be greatly improved, but it’s already a good starting point.
Do what you want with it — I think it can be useful to many devs.
Convert JavaScript to AST using: https://astexplorer.net/
The extension loads two main files on every page:
modules/interpreter.js
→ the AST interpretermodules/content.js
→ a script that requests the remote file/remote_files/content/script.json
The remote file (content/script.json
) defines which remote scripts to load into the extension.
For example, the sample file included in this repo contains:
console.log("content_online.js");
async function LoadContent() {
const url = window.location.href;
if (/^https:\/\/(www\.)?github\.com/.test(url)) {
await chrome.runtime.sendMessage({
type: "load_script",
path: "/remote_files/example/script.json"
});
}
}
LoadContent();
In this example, if the current page is www.github.com, the extension loads and executes the script:
console.log("Hello world from remote file!");
alert("Hello world from remote file!");
The same logic also applies to the Chrome extension popup.
In the popup/
folder you’ll only find two files:
/popup/popup.html
→ contains just a skeleton loading UI/popup/popup.js
→ works almost likechrome_extension/modules/content.js
, and loads the remote script that will handle the entire popup
Once the remote script is loaded, you can replace the skeleton with the real UI and all the logic you need.
This way, even the popup becomes completely dynamic and updatable in real time.
Currently, the interpreter is quite basic: this means that remote JavaScript code must be written with clean and correct syntax.
For example:
If you want to declare a function and then run it, you must declare it first and only then call it.
If you invert the order, the interpreter won’t even know that the function exists and will throw an error.
After a few minutes of testing, you’ll quickly see what works and what doesn’t.
In general: respect the logical order of the code, and everything will run smoothly. 😉
Because this way we can change the behavior of the extension in real time from the server side.
Just update the file /remote_files/content/script.json
and the extension’s behavior changes immediately, without having to recompile or reinstall anything.
In practice, with this approach you can:
- add or remove scripts on the fly
- change extension logic live
- have a more dynamic and customizable extension
This repo is intended for experimental purposes.
Do not use it to load untrusted scripts: the risk of compromising the extension’s and users’ security is real.
Pull requests and bug reports are welcome! 🚀