Skip to content

Chrome Extension Analytics: What to Track and What Never To

6 min readModerok team

Do you need analytics in a Chrome extension? What to track, in what order, and the data Chrome's own policies forbid you from collecting.

You need analytics in a Chrome extension if you cannot answer three questions from the Web Store dashboard: how many people still use the extension, which features they use, and what breaks for them. The store's own metrics page says the Users stat "only captures installations; it doesn't monitor whether users are active or not". So the short answer on what to track is: lifecycle, one activation event, one event per feature, and errors. The shorter answer on what never to track is: browsing activity, free-form user content, and anything that names a person.

TL;DR: Instrument four things and stop. Lifecycle (install, update, uninstall, daily activity) turns an install count into a retention number. One activation event tells you whether new installs reach the point of the extension. One event per feature tells you what to keep. Errors tell you why people leave. Never collect web browsing activity: Chrome's Limited Use policy says that collection "is prohibited, except to the extent required for a user-facing feature". Never send free-form text or an identifier you could look someone up with.

Do you need analytics in a Chrome extension at all?

Start by subtracting what you already have, since the Web Store dashboard is already running. Per the store listing metrics docs, it gives you daily installs and daily uninstalls broken out by country, language and operating system, impressions for how often people find your listing in the store, and weekly user retention grouped by country, language, operating system and item version. You can also opt in to a Google Analytics 4 property from the Store listing tab, which the same page describes as non user-level data.

That answers acquisition. It answers nothing about the product itself:

QuestionWeb Store dashboardYour own events
How many installs yesterdayYesYes
How many of those installs are still usedNo: (Users counts installations)Yes
Did anyone open the feature I shipped last weekNoYes
What exception is firing in my service workerNoYes

If the rows marked No are questions you are currently guessing at, that is the case for adding analytics. If your extension is one button and you are not planning to change it, the dashboard may genuinely be enough.

What to track, in priority order

The order matters more than the list: each tier is the denominator for the next, so it is only worth instrumenting once the one above it is in place.

1. Lifecycle

Install, update, uninstall, and a once-a-day signal that the extension is still in use. These turn a raw install count into a retention number, and they are the tier most likely to be handled for you: Moderok's SDK sends __first_open, __install, __update, __daily_ping and __error without any track() calls, per the automatic events docs. Uninstall tracking is separate and opt-in, via the trackUninstalls option that sets an uninstall URL with chrome.runtime.setUninstallURL.

If you are rolling your own, the daily signal is the fiddly one. Advancing the stored date when you enqueue the ping marks the day as counted even if the request is later rejected and never retried, so advance it only after the server accepts the event, and check whether today's ping is already in the recovered queue first.

2. One activation event

Pick the single action that means "this person got the point of the extension" and fire one event for it: for a screenshot tool, the first capture. Nothing else in your schema is worth as much, because it splits installs into people who tried the thing and people who never came back, and those two groups need different fixes.

3. One event per feature you might remove

Not per click target. Per user intention, named for what happened:

import { Moderok } from "@moderok/sdk";

Moderok.track("export_completed", { format: "csv", item_count: 42 });

The test is whether the number could change a decision. If you would not remove the feature no matter how low the count came back, you do not need the event yet. Naming, the MV3 click handlers that silently never fire, and how to read a count as adoption are in Chrome extension usage tracking.

4. Errors

An extension's failure mode is usually silence: a service worker throws, the popup shows nothing, the user removes it without telling you. Uncaught exceptions and unhandled rejections become __error events in every context where you call Moderok.init(), since trackErrors defaults to true. Callback failures are not, because chrome.runtime.lastError is not thrown:

chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
  if (chrome.runtime.lastError) {
    Moderok.captureLastError("tabs.query", chrome.runtime.lastError, { action: "load_active_tab" });
    return;
  }
  Moderok.track("active_tab_loaded", { count: tabs.length });
});

The error tracking docs also cover captureError(error, props?) for try/catch blocks, and captureConsoleErrors for mirroring console.error, off by default because it is noisy.

What never to track

Two of these are policy, not style.

Web browsing activity. The Chrome Web Store user data FAQ defines it as "any information about the websites or other web resources a user requests or interacts with", and says an extension can only collect and transmit it to the extent required for a user-facing feature. The Limited Use policy repeats the prohibition and adds that extensions "may only collect, use, or transmit user data that is necessary for the extension's disclosed single purpose". Analytics curiosity is not a user-facing feature. So no URLs, no hostnames, no page titles, and no referrers as event properties, even though a content script can read all four.

Anything that names a person. The same FAQ lists name, address, telephone number, email address and username as user data. An anonymous analytics id exists so you can count distinct people without storing any of that. Attaching an account email to events removes the only property that made the dataset safe to hold.

Free-form user content. Search queries, note text, clipboard contents, file names, and form input. You cannot predict what a user typed, so you cannot predict what you are storing. Send the shape instead of the content: { query_length: 12, had_results: true } answers almost every product question that the raw string would.

The error payload you did not read. An error event carries the exception's message, name and a truncated stack. A caught exception whose message interpolates the user's input is the free-form case again, arriving through a different door, so read your own throw sites before you turn capture on. Add deliberate properties to captureError rather than widening what you send by accident.

Collecting user data at all brings a disclosure duty with it. The FAQ says you must describe the types of user data collected and how they will be used and obtain the user's consent, that the disclosure and consent "must occur within the Product's user interface", and that it "must not be located only in a privacy policy, terms of service, or similar document". A store description does not satisfy it either. Consent law in your users' jurisdictions is a separate question again: see Chrome extension analytics and GDPR.

Keep the payload deliberately small

Two constraints push the same way. The events docs require flat properties, with each value a string, number or boolean, and nested objects, arrays, null and non-finite numbers dropped. And Moderok's configuration reference caps a single event's JSON at 8 KiB, skipping oversized events, with an in-memory queue of 1000 events and 500 persisted pending events. Error events are capped at 20 per minute per JS context, manual captureError calls included, and a repeat of the same error inside that window is dropped rather than sent twice.

Those limits are generous for counts and small for content: four scalar properties will never come near 8 KiB, and a document body will.

Start with the four tiers

Instrument lifecycle, one activation event, a handful of feature events and errors, and you can answer every question in that table's right-hand column without collecting anything you would be uncomfortable explaining in a privacy policy. Moderok sends the install, update, daily-activity and error events by default, with uninstall tracking one option away, and needs only the storage permission to do it: see the getting started guide for the setup steps, or what the dashboard reports before you write any code.