Integration Guides

Chrome extension

Add Rybbit analytics to your Chrome extension

Manifest V3 requires every piece of executable code to ship inside the extension package, and the extension_pages Content Security Policy is fixed at script-src 'self' 'wasm-unsafe-eval'; object-src 'self' with no way to relax it. A remote <script> tag can therefore never load in an extension page. Bundle the @rybbit/js SDK into your popup, options or side panel page instead, and grant host_permissions for the analytics host.

Get your tracking snippet

In your Rybbit dashboard, open Site Settings → Tracking Script and copy your snippet. It looks like this:

<script src="https://app.rybbit.io/api/script.js?siteId=YOUR_SITE_ID" defer></script>

YOUR_SITE_ID is the numeric ID of your site. If you self-host Rybbit, app.rybbit.io is the domain of your own instance.

You only need the site ID from the snippet; the script tag itself is not used in an extension.

Add the snippet to Chrome extension

  1. Install the SDK in the extension project:
npm install @rybbit/js
  1. Initialise it in the entry script of each extension page you want to track (the popup from action.default_popup, the options page from options_ui.page, the side panel from side_panel.default_path). The SDK needs window and document, so do not import it in the service worker.
src/popup.js
import rybbit from "@rybbit/js";

await rybbit.init({
  analyticsHost: "https://app.rybbit.io/api",
  siteId: "YOUR_SITE_ID",
});

// Extension pages have URLs like chrome-extension://<id>/popup.html.
// Report a readable path instead:
rybbit.pageview("/popup");
  1. Grant the extension permission to call the analytics host. Requests from extension pages to origins listed in host_permissions are not subject to CORS:
manifest.json
{
  "manifest_version": 3,
  "host_permissions": ["https://app.rybbit.io/*"]
}
  1. Build the extension with your bundler (Vite, webpack, Rollup) so @rybbit/js ends up inside the package, then reload the unpacked extension at chrome://extensions.

The default extension CSP does not restrict connect-src, so no content_security_policy entry is required. If you define your own policy, add https://app.rybbit.io to its connect-src.

Do not initialise the SDK in a content script. Content scripts run inside third-party websites; tracking there records other people's pages under your site ID, and content scripts are subject to the host page's same-origin policy in any case.

Verify installation

Open your live site in a new tab and click through a few pages. Within a few seconds the pageviews appear in the Rybbit dashboard.

If nothing shows up:

  • View the page source and search for script.js?siteId= to confirm the snippet is on the page.
  • Open the browser Network tab and check that script.js returns 200 and that POST requests go to /api/track.
  • Disable ad blockers, or set up a proxy so the script loads from your own domain.
  • See the script troubleshooting guide for other common causes.

Track custom events

Call rybbit.event() from the page script, or use data attributes on buttons in your popup HTML:

src/popup.js
document.getElementById("save")?.addEventListener("click", () => {
  rybbit.event("settings_saved", { theme: "dark" });
});
popup.html
<button data-rybbit-event="upgrade_click" data-rybbit-prop-plan="pro">Upgrade</button>

Troubleshooting

  • Refused to load the script in the popup console: the fixed extension_pages CSP blocked a remote tag. Remove it and use the SDK.
  • POST /api/track blocked by CORS: host_permissions is missing or does not match. The pattern must include the path wildcard: https://app.rybbit.io/*.
  • Self-hosted Rybbit: replace https://app.rybbit.io in both analyticsHost and host_permissions with your own instance URL.
  • Every pageview shows the extension ID as hostname: expected. Extension pages live on chrome-extension://<id>; use rybbit.pageview("/name") for readable paths.

Next steps

On this page