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 your site's ID, a 12-character string such as a1b2c3d4e5f6. Older sites may use a numeric ID, which still works. 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",
});

init() records a pageview for the page by itself (unless Initial Page View is turned off for the site), so don't call rybbit.pageview() as well or each open counts twice. Extension pages have URLs like chrome-extension://<id>/popup.html, so the popup shows up as /popup.html.

  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>; the path (/popup.html, /options.html) still tells pages apart. For custom paths, turn off Initial Page View under Site Settings → Tracking and call rybbit.pageview("/name") after init() in each page you track. The setting applies to every page using this site ID, not only the popup.

Next steps

On this page