Google Analytics 4 in Chrome Extensions: Complete Tracking Guide (2026)
Adding Google Analytics 4 to Your Chrome Extension
Understanding how users interact with your extension is essential for growth. Google Analytics 4 provides the insights you need, but Manifest V3 prohibits loading remote scripts — so you can’t use the standard gtag.js snippet.
The solution: GA4’s Measurement Protocol, a server-side API that lets you send events directly from your extension’s code.
Setup: Get Your Credentials
- Create a GA4 property at analytics.google.com
- Go to Admin → Data Streams → Web → Create a stream
- Note your Measurement ID (starts with
G-) - Go to Admin → Data Streams → Select stream → Measurement Protocol API secrets
- Create a new API secret and save it
{
"manifest_version": 3,
"permissions": ["storage"]
}Core Analytics Module
Create a reusable analytics module for your extension:
// analytics.js
const GA_ENDPOINT = 'https://www.google-analytics.com/mp/collect';
const GA_DEBUG_ENDPOINT = 'https://www.google-analytics.com/debug/mp/collect';
const MEASUREMENT_ID = 'G-XXXXXXXXXX';
const API_SECRET = 'your_api_secret';
const DEBUG = false;
async function getOrCreateClientId() {
const result = await chrome.storage.local.get('ga_clientId');
if (result.ga_clientId) return result.ga_clientId;
const timestamp = Math.floor(Date.now() / 1000);
const randomPart = Math.floor(Math.random() * 2147483647);
const clientId = `${randomPart}.${timestamp}`;
await chrome.storage.local.set({ ga_clientId: clientId });
return clientId;
}
async function getOrCreateSessionId() {
const SESSION_TIMEOUT_MIN = 30;
let { sessionData } = await chrome.storage.session.get('sessionData');
const now = Date.now();
if (sessionData && now - sessionData.timestamp < SESSION_TIMEOUT_MIN * 60 * 1000) {
sessionData.timestamp = now;
await chrome.storage.session.set({ sessionData });
return sessionData.sessionId;
}
sessionData = {
sessionId: now.toString(),
timestamp: now
};
await chrome.storage.session.set({ sessionData });
return sessionData.sessionId;
}
async function sendEvent(eventName, params = {}) {
const clientId = await getOrCreateClientId();
const sessionId = await getOrCreateSessionId();
const endpoint = DEBUG ? GA_DEBUG_ENDPOINT : GA_ENDPOINT;
const body = {
client_id: clientId,
events: [{
name: eventName,
params: {
session_id: sessionId,
engagement_time_msec: '100',
...params
}
}]
};
const url = `${endpoint}?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
try {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(body)
});
if (DEBUG) {
const debugResponse = await response.json();
console.log('GA4 Debug:', debugResponse);
}
} catch (error) {
console.error('GA4 send failed:', error);
}
}Tracking Common Events
Extension Installation and Updates
// service-worker.js
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
sendEvent('extension_installed', {
version: chrome.runtime.getManifest().version
});
} else if (details.reason === 'update') {
sendEvent('extension_updated', {
previous_version: details.previousVersion,
current_version: chrome.runtime.getManifest().version
});
}
});Popup Opens and Page Views
// popup.js
sendEvent('page_view', {
page_title: 'Popup',
page_location: 'chrome-extension://popup.html'
});Feature Usage
// Track button clicks
document.getElementById('analyze-btn').addEventListener('click', () => {
sendEvent('feature_used', {
feature_name: 'analyze_extension',
trigger: 'popup_button'
});
});
// Track settings changes
function onSettingChanged(settingName, newValue) {
sendEvent('setting_changed', {
setting_name: settingName,
setting_value: String(newValue)
});
}Errors and Performance
// Track errors (avoid reserved name "error")
function trackError(context, message) {
sendEvent('extension_error', {
error_context: context,
error_message: message.substring(0, 100)
});
}
// Track performance
function trackTiming(category, duration) {
sendEvent('performance_timing', {
timing_category: category,
timing_value: Math.round(duration)
});
}Reserved Event Names to Avoid
GA4 reserves certain event names. Using them causes silent failures:
| Don’t Use | Use Instead |
|---|---|
error | extension_error |
login | user_login |
search | user_search |
share | content_share |
sign_up | user_sign_up |
Debugging
Use the debug endpoint to validate events before production:
- Set
DEBUG = truein your analytics module - Send test events
- Check the response for validation messages
// Example debug response
{
"validationMessages": [
{
"fieldPath": "events[0].params.session_id",
"description": "Value must be a valid string.",
"validationCode": "VALUE_INVALID"
}
]
}Also check GA4’s Realtime report to see events arrive within seconds.
Privacy Considerations
- Disclose analytics in your privacy policy — Required by Chrome Web Store policy
- Provide an opt-out mechanism:
async function isAnalyticsEnabled() {
const { analyticsOptOut } = await chrome.storage.sync.get('analyticsOptOut');
return !analyticsOptOut;
}
async function sendEvent(eventName, params = {}) {
if (!(await isAnalyticsEnabled())) return;
// ... rest of send logic
}- Don’t track PII — Never send emails, URLs with user data, or personal identifiers
- Respect Do Not Track — Consider honoring the browser’s DNT header
What’s Next
GA4 Measurement Protocol gives you full analytics capabilities without violating MV3’s remote code restrictions. Track installations, feature usage, and errors to make data-driven decisions about your extension’s development.
For more ways to grow your extension, explore ExtensionBooster’s developer tools.
Share this article
Build better extensions with free tools
Icon generator, MV3 converter, review exporter, and more — no signup needed.
Related Articles
I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
WXT vs Plasmo vs CRXJS vs Extension.js vs Bedframe. Real benchmarks, honest opinions, and the framework with 12K stars that's quietly dying.
5 Best Email Marketing Services to Grow Your Chrome Extension (2026)
Compare the top email marketing platforms for SaaS and Chrome extension developers. MailerLite, Mailchimp, Brevo, ActiveCampaign, and Drip reviewed.
15 Best Practices to Build a Browser Extension That Users Love (2026 Guide)
Master browser extension development in 2026. Manifest V3, security, performance, and UX best practices to build extensions users love.