Skip to content

Extension-useLet anything drive your browser extension

Packages you add to your own extension, so a web app, a website, a script or an AI agent can read pages and act on them through a typed, declarative set of actions. Your extension keeps the privileges.

How it fits together ​

web app (iframe in side panel)  ──postMessage──▢  frame bridge ─┐
website (externally_connectable) ────────────────────────────────▢ router (background) ──▢ content runtime (tab)
script / agent (remote)         ──relayβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        grants Β· policy Β· audit

Your extension runs the host: a router in the background, a runtime in each page, and a bridge for your web app. Controllers connect from outside and call actions by name. They can only call the actions you enable, on the sites your policy allows. No controller code ever runs inside the extension, so it stays within Chrome Web Store rules.

In your extension and in your app ​

ts
// Your extension: background
import { defineHost } from "@extension-use/host/background";

defineHost({
  id: "my-extension",
  controllers: { frame: { origins: ["https://app.example.com"] } },
  contentScript: "content.js",
});
ts
// Your web app
import { connect } from "@extension-use/client";
import { frame } from "@extension-use/client/frame";

const ext = await connect(frame());
const { text } = await ext.dom.snapshot({ format: "text" }); // the page as a tree with refs
await ext.dom.click({ locator: { role: "button", name: "Save" } });

Not sure which packages you need? The package picker asks three questions and writes the setup for you.

The SDK packages are released under the MIT license.