Skip to main content
Chrome Extensions have special security constraints. Our SDK provides a dedicated transport layer that handles message passing between content scripts, background scripts, and web pages.

Overview

The Composite Analytics SDK includes first-class support for Chrome Extensions with:
  • Automatic Content Script Transport - Handles message passing between isolated contexts
  • Manifest V3 Support - Full compatibility with the latest Chrome Extension manifest
  • Session Recording - Capture user interactions across all tabs
  • User Identification - Identify users across extension contexts
  • Optimized Batching - Efficient batching to minimize API calls

Installation

Install the SDK in your Chrome Extension project:
npm install @composite-inc/composite-js

Manifest Configuration

Add the required permissions and host permissions to your manifest.json:
  • Manifest V3
  • Manifest V2
{
  "manifest_version": 3,
  "name": "Your Extension",
  "version": "1.0.0",
  "permissions": [
    "storage",
    "tabs"
  ],
  "host_permissions": [
    "https://prod.alb.us.api.composite.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}

Basic Setup

Background Script

Initialize the SDK in your background script (service worker for Manifest V3):
// background.js
import composite from '@composite-inc/composite-js';

// Initialize with Chrome Extension transport
chrome.runtime.onInstalled.addListener(async () => {
  await composite.init({
    apiKey: 'pk_your_api_key',
    transport: 'chrome-extension',  // Automatically uses ContentScriptTransport
    debug: true
  });
});

Content Script

Enable session recording from content scripts that run on web pages:
// content.js
import composite from '@composite-inc/composite-js';

// Initialize in content script with session recording
(async () => {
  await composite.init({
    apiKey: 'pk_your_api_key',
    transport: 'chrome-extension',
    sessionRecording: true
  });
})();
Initialize the SDK from your extension’s popup:
// popup.js
import composite from '@composite-inc/composite-js';

document.addEventListener('DOMContentLoaded', async () => {
  await composite.init({
    apiKey: 'pk_your_api_key',
    transport: 'chrome-extension'
  });

  // Identify user when they log in
  document.getElementById('login-button')?.addEventListener('click', async () => {
    const user = await authenticateUser();
    composite.identify(user.id, {
      email: user.email,
      plan: user.plan
    });
  });
});

Session Recording in Extensions

Enable session recording to capture user interactions across all tabs:
// content.js - Session recording setup
import composite from '@composite-inc/composite-js';

await composite.init({
  apiKey: 'pk_your_api_key',
  transport: 'chrome-extension',
  sessionRecording: {
    maskAllInputs: true,        // Mask sensitive input fields
    blockSelector: '.sensitive', // Block specific elements
    maskTextSelector: '.private', // Mask text content
    compress: true,              // Enable compression
    recordCanvas: false,         // Disable canvas recording for performance
    recordCrossOriginIframes: false
  }
});

// Manually control recording
composite.startSessionRecording();  // Start recording
composite.stopSessionRecording();   // Stop recording
Session recording can impact performance. Use selectors to mask sensitive content and consider disabling canvas recording for better performance.

Advanced Configuration

Cross-Context Communication

The SDK handles message passing between different extension contexts automatically:
// Shared configuration for all contexts
const compositeConfig = {
  apiKey: 'pk_your_api_key',
  transport: 'chrome-extension',
  sessionRecording: true,
  debug: process.env.NODE_ENV === 'development',

  // Custom transport configuration
  transportOptions: {
    flushInterval: 2000,     // Batch events every 2 seconds
    maxBatchSize: 50,        // Max events per batch
    retryLimit: 3,           // Retry failed requests
    timeout: 10000           // Request timeout
  }
};

// Use same config in all contexts
await composite.init(compositeConfig);

Storage and Persistence

The SDK automatically uses Chrome’s storage API for persistence:
// User identification persists across sessions
composite.identify('user_123', {
  email: 'user@example.com',
  subscription: 'premium'
});

// Reset on logout
document.getElementById('logout')?.addEventListener('click', () => {
  composite.reset();  // Clears user identity
});

User Identification

Identify users across extension contexts:
// Identify user when they authenticate
async function onUserLogin(user) {
  composite.identify(user.id, {
    email: user.email,
    plan: user.plan,
    created_at: user.createdAt
  });
}

// Reset on logout
document.getElementById('logout')?.addEventListener('click', () => {
  composite.reset();  // Clears user identity
});

Build Tool Configuration

Choose your build tool setup:
  • Vite
  • Webpack
  • Plain JavaScript
Chrome extensions require different build configurations for content scripts vs background/popup scripts:
// vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';

// Content script needs IIFE format (no ES modules in content scripts)
const contentConfig = {
  build: {
    rollupOptions: {
      input: {
        content: resolve(__dirname, 'src/content.js')
      },
      output: {
        format: 'iife',
        entryFileNames: '[name].js',
        inlineDynamicImports: true
      }
    },
    outDir: 'dist',
    emptyOutDir: false
  }
};

// Background and popup can use ES modules
const mainConfig = {
  build: {
    rollupOptions: {
      input: {
        background: resolve(__dirname, 'src/background.js'),
        popup: resolve(__dirname, 'src/popup.js')
      },
      output: {
        entryFileNames: '[name].js',
        chunkFileNames: '[name].js',
        assetFileNames: '[name].[ext]'
      }
    },
    outDir: 'dist',
    emptyOutDir: false
  }
};

// Export based on mode: `vite build` vs `vite build --mode content`
export default defineConfig(({ mode }) => {
  if (mode === 'content') {
    return contentConfig;
  }
  return mainConfig;
});
Build both configurations:
# Build background and popup scripts
vite build

# Build content script separately
vite build --mode content
Content scripts run in an isolated context and cannot use ES modules. Use separate build configs with format: 'iife' and inlineDynamicImports: true for content scripts.

Dynamic Loading

Load the SDK only when needed:
// Lazy load for popup
document.addEventListener('DOMContentLoaded', async () => {
  if (userHasOptedIn()) {
    const { default: composite } = await import('@composite-inc/composite-js');
    await composite.init({
      apiKey: 'pk_your_api_key',
      transport: 'chrome-extension'
    });
  }
});

Debugging

Enable debug mode to see detailed logs:
await composite.init({
  apiKey: 'pk_your_api_key',
  transport: 'chrome-extension',
  debug: true  // Enables console logging
});

// Check transport status
console.log('Transport ready:', composite.isInitialized());

// Manually flush events
await composite.flush();

Chrome DevTools

  1. Open Chrome DevTools for your extension
  2. Check the Network tab for requests to prod.alb.us.api.composite.com
  3. Use the Console to see debug logs
  4. Check Application > Storage for persisted data

Common Issues

If you encounter CSP errors, ensure your manifest includes the correct host permissions:
"host_permissions": [
  "https://prod.alb.us.api.composite.com/*"
]
For inline scripts, you may need to adjust your CSP:
"content_security_policy": {
  "extension_pages": "script-src 'self'; object-src 'self'"
}
The SDK handles message passing automatically, but ensure:
  • Background script is loaded before content scripts
  • Same API key is used across all contexts
  • Transport is set to 'chrome-extension' in all contexts
Session recording requires:
  • Content script injected with <all_urls> or specific matches
  • sessionRecording: true in configuration
  • Sufficient permissions in manifest
Test with a simple page first, then expand to more complex sites.
Check:
  1. API key is correct
  2. Network requests are successful (check DevTools)
  3. Debug mode is enabled to see logs
  4. Session recording is enabled in config

Best Practices

Minimize Permissions

Only request the permissions your extension actually needs

Mask Sensitive Data

Use selectors to mask passwords, credit cards, and PII in recordings

Handle Errors

Implement error boundaries and fallbacks for SDK failures

Identify Users

Always identify logged-in users to connect their sessions

Example: Complete Extension

Here’s a complete example of a Chrome Extension with Composite Analytics:
import composite from '@composite-inc/composite-js';

// Initialize on install
chrome.runtime.onInstalled.addListener(async (details) => {
  await composite.init({
    apiKey: 'pk_your_api_key',
    transport: 'chrome-extension'
  });
});

What’s Next?