Skip to content

How to Migrate a Chrome Extension from Google Analytics to Moderok

6 min readModerok team

How to migrate a Chrome extension from Google Analytics to Moderok: what maps across, what plumbing you delete, and why your user count jumps.

If Google Analytics is already running in your Chrome extension through the GA4 Measurement Protocol, the migration to Moderok is three code changes and one decision. Install @moderok/sdk, call Moderok.init() at the top level of your background service worker, and delete the helper that POSTs to /mp/collect. The storage permission is the one Moderok needs too, so the manifest does not change: Chrome's guide has you keep the GA4 client ID in browser.storage.local (developer.chrome.com, as of October 2026). The decision is what to do with the history in your GA4 property, because it does not travel with you.

TL;DR: Event names and properties port over nearly unchanged, the client ID, session and retry plumbing gets deleted, and on release day every existing user gets a fresh anonymous id and counts as a new user.

The 30-second comparison

What your GA4 setup hasWhat replaces itSource
A client_id you generate and store yourselfA random anonymous id the SDK creates in chrome.storage.local and mirrors to chrome.storage.syncThe client ID "can be an arbitrary string, but should be unique to the client" (developer.chrome.com, as of October 2026)
measurement_id and api_secret in the request URLOne appKey passed to init()"To send events to Google Analytics, you need an api_secret and a measurement_id" (developer.chrome.com, as of October 2026)
session_id and engagement_time_msec on every eventNothing to send; a __daily_ping at most once per UTC dayBoth "required for user activity to display in standard reports like Realtime" (developer.chrome.com, as of October 2026)
Your own batching and retry code, 25 events per requestA capped queue, batchSize 20 and flushInterval 30000 by default"Requests can have a maximum of 25 events" (developers.google.com, as of October 2026); Configuration
Event data retention of 2 or 14 months on a standard propertyEvents kept with no time limit on every planLonger options are 360 only (support.google.com, as of October 2026); Privacy

Step 1: keep Google Analytics running while you migrate the Chrome extension

The SDK does not touch your GA4 code path, so leave the /mp/collect helper in place while you add it: two readings of the same week, to compare before deleting anything.

Setup is three parts: add the package, keep "permissions": ["storage"] in the manifest, initialize. The SDK is 5.7 kB gzipped (5,671 bytes, measured 2 August 2026) with zero runtime dependencies.

// background.js
import { Moderok } from "@moderok/sdk";

Moderok.init({ appKey: "mk_your_app_key_here" });

That call belongs at the top level of the service worker, before any await: the SDK registers its chrome.runtime.onInstalled listener during the call, and init() running after async work can miss __install and __update (Initialization).

Step 2: map your event names and properties

Moderok.track(name, properties) takes the same shape of payload you assembled for the GA4 events array (developers.google.com, as of October 2026), so call sites get shorter:

// Replaces: build the MP body, POST it, handle the retry.
Moderok.track("export_clicked", { format: "csv", rows: 412 });

The naming rules change. Chrome's guide calls out the reservation you probably worked around: "'error' is a reserved event name and cannot be used," which is why its sample sends extension_error instead (developer.chrome.com, as of October 2026). The events the SDK sends itself all start with a double underscore (__first_open, __install, __update, __daily_ping, __error) and the docs ask you to avoid those names (Automatic events), so extension_error still works and so does plain error. GA4 allows a "maximum of 25 parameters" per event (developers.google.com, as of October 2026). Moderok's documented ceiling is a single event JSON size of 8 KiB, with oversized events skipped (Configuration), and the ingestion API caps how many properties one event carries, so port a wide GA4 payload as a handful of meaningful keys.

Property values must be flat: string, number or boolean. Nested objects, arrays and null are dropped, and debug: true logs what was stripped (Events).

Two groups of parameters you can stop sending. First, page_view, which Chrome's guide notes "also requires the page_title and page_location parameters" (developer.chrome.com, as of October 2026), so you invent a URL for something that was never a page. Second, the context you attached by hand: the SDK puts browser, browser version, OS, locale, extension ID, extension version, SDK version and the calling context (background, popup, options, side panel, content script, extension page) on every event, in each event's context rather than as chart breakdowns.

Step 3: delete the plumbing

This is where the migration pays for itself. All of it exists because the Measurement Protocol made you build it:

  • The client ID. Chrome's guide has you generate one and keep it in browser.storage.local: "The ID should stay the same, as long as the extension is installed on a user's browser" (developer.chrome.com, as of October 2026). The SDK handles its own identity.
  • Sessions. The same guide says a session "ends after 30 minutes of user inactivity" by default and that, because extensions have no obvious session, "you must define what a user session means in your extension" (developer.chrome.com, as of October 2026). Moderok has no session concept to define, and no engagement_time_msec bookkeeping either.
  • Your queue and retry loop. The SDK batches on an interval and when the batch fills, caps the in-memory queue at 1000 events, persists up to 500 pending events across worker restarts, and retries retryable failures with backoff (Configuration). It is a best-effort queue, not a delivery guarantee.
  • Error reporting. trackErrors defaults to true, so uncaught exceptions and unhandled rejections in any context where you called init() become __error events. Handled failures go through Moderok.captureError(error, props), and callback APIs through Moderok.captureLastError("tabs.query", chrome.runtime.lastError) (Error tracking).

The file to delete is the one holding the /mp/collect POST and the client-ID helper.

Step 4: expect a new-user spike on release day

Existing users have no Moderok id yet, so the first time the updated extension runs the SDK creates one and sends __first_open. Everyone you already had counts as new that week, even though the lifecycle event they fire is __update (with previousVersion) and not __install. Cohort and retention readings for that week describe your rollout, not new users.

Lifecycle instrumentation stops being your job: __install, __update, __first_open and the daily ping come from the SDK (Automatic events), and trackUninstalls registers chrome.runtime.setUninstallURL for you (Uninstall tracking).

Two numbers to know before comparing dashboards: Moderok's install count will not match the Chrome Web Store's (Installs vs Chrome Web Store), and uninstall pings are a floor rather than a count.

Step 5: decide what happens to your GA4 history

Moderok does not import GA4 data, and GA4 will not hold it forever. Event-level retention on a standard property can be set to 2 or 14 months, the 26, 38 and 50 month options being 360 only (support.google.com, as of October 2026). For raw rows, BigQuery Export is the documented route: "Standard properties have a daily BigQuery Export limit of 1 million events" and exports beyond the sandbox limits "incur charges" (support.google.com, as of October 2026).

The low-effort option is to stop sending and leave the property as an archive until retention expires.

When Google Analytics is the right choice

If your extension is one surface of a product whose website already reports into GA4, keeping both in the same property is worth real effort to give up: one place to look, one vendor, the reports your team already reads.

Google's documentation adds a note. The Measurement Protocol overview says its intent is "to augment automatic collection through gtag, Tag Manager, and Google Analytics for Firebase," explicitly "not to replace it," and warns that sending events "solely with the Measurement Protocol" can leave "only partial reporting" available (developers.google.com, as of October 2026). An extension has no other way in: "you have to use the Google Analytics Measurement Protocol for tracking extension events" (developer.chrome.com, as of October 2026). If the website does the heavy reporting and the extension is a side channel, fine. If the extension is the product, you are building on the augmentation path, not the main one.

For a wider read: the head-to-head on Moderok vs Google Analytics, the roundup of extension analytics tools, and what the dashboard reports.

To try the swap, the getting started guide is the three-part setup above, and your existing GA4 code can stay where it is while you compare.