Amplitude TypeScript SDKs

repository·main·Indexed 41 days ago

https://github.com/amplitude/amplitude-typescript

A collection of specialized TypeScript SDKs for integrating Amplitude analytics across different environments, including Web (@amplitude/analytics-browser v2.45.10), Node.js, and React Native. Includes implementation examples for Next.js, React, Vue.js, NestJS, and Expo, as well as an Express-based proxy server for intercepting and forwarding tracking requests.

Tokens
74.4K
Snippets
275
Records
367
Agent score
52%

What's inside Amplitude

  1. Identify the correct Amplitude SDK for your platform

    main

    Amplitude provides specialized TypeScript SDKs depending on your runtime environment. Choose the package that matches your project type:

    • Web/Browser: Use @amplitude/analytics-browser (v2 is the latest).
    • Node.js: Use @amplitude/analytics-node.
    • React Native: Use @amplitude/analytics-react-native.

    For detailed installation and quick start guides, refer to the official Amplitude developer documentation for your specific platform.

  2. How the Experiment plugin integrates with Analytics

    main

    The plugin utilizes Experiment.initializeWithAmplitudeAnalytics to connect the Experiment SDK to the Analytics identity store and event bridge.

    Key behaviors:

    • No Duplication: It does not duplicate identity or exposure tracking logic.
    • Lifecycle: The Experiment client is tied to the plugin; removing the plugin will stop the Experiment client.
  3. Configure Cross-Origin Iframe Recording

    main

    The SDK can capture events inside cross-origin <iframe> elements and merge them into the parent page's session replay.

    Requirements:

    • Both the parent page and each iframe page must load the SDK.
    • Both must be initialized with crossOriginIframes.enabled: true.
    • Third-party iframes (e.g., Stripe, Google Maps) cannot be captured because they do not load your SDK.

    How it works:

    • The parent SDK uses a CrossOriginIframeCoordinator to watch for iframe additions/removals and sends postMessage signals (start/stop) to children.
    • The child SDK detects it is in an iframe (isInIframe() === true) and enters 'child mode', waiting for the parent's signal before recording.
    • Child events are relayed via postMessage and merged into the parent's stream.

    Privacy Note: Privacy settings (mask levels, block selectors) configured on the parent do not automatically apply to the iframe. You must configure privacy settings independently on the child page.

    // Parent page setup
    sessionReplay.init(API_KEY, {
      deviceId: DEVICE_ID,
      sessionId: SESSION_ID,
      crossOriginIframes: { enabled: true },
    });
    
    // Child iframe page setup (must also load the SDK)
    sessionReplay.init(API_KEY, {
      deviceId: DEVICE_ID,
      sessionId: SESSION_ID,
      crossOriginIframes: { enabled: true },
    });
  4. How ShadowGate latching works

    main

    The ShadowGate (implemented in src/shadow-mode.ts) manages the lifecycle of shadow support for a page session using a latching mechanism:

    1. Initial State: Shadow support is off by default.
    2. Arming: On the first remote config delivery that sets shadowDomEnabled: true, the gate 'arms'.
    3. Latch Behavior: Once armed, subsequent config deliveries that attempt to disable shadow support will have no effect until the next full page load.
    4. Depth Locking: The maxShadowDomDepth value is fixed based on the first delivery that arms the gate for the rest of the page session.
    5. One-time Scan: When the gate arms, an onArm subscriber performs a one-time scan of the existing DOM to attach MutationObserver instances to any already-present open shadow roots.
  5. How the autocapture transformation works

    main

    The plugin automatically wraps React Native interaction handlers with Amplitude's ampCapture helper at compile time. This allows UI events to be captured without manual code changes in your JSX.

    Transformation Logic

    The plugin only transforms known interactive JSX elements that have an expression handler (e.g., onPress={func}). It ignores string handlers and elements without handlers.

    Supported Categories

    CategoryDefault elementsAttributes → action
    PressableButton, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, Pressable, Touchable, TouchableNativeFeedback, LinkonPress → Press, onLongPress → LongPress
    Value changeSwitch, Slider, PickeronValueChange → ValueChange
    Text changeTextInputonChangeText → ChangeText, onSubmitEditing → SubmitEditing

    Metadata Captured

    If the element has accessibilityLabel or testID, these are copied into the ampCapture properties object. The plugin also automatically adds:

    • element: The JSX tag name (e.g., Button).
    • component: The enclosing React component or function name (e.g., HomeScreen).

    Example

    Input JSX:

    function HomeScreen() {
      return (
        <Button
          accessibilityLabel="Online test label"
          testID="12345"
          title="Online test"
          onPress={func}
        />
      );
    }

    Transformed Output:

    import { ampCapture } from '@amplitude/analytics-react-native';
    
    function HomeScreen() {
      return (
        <Button
          accessibilityLabel="Online test label"
          testID="12345"
          title="Online test"
          onPress={ampCapture(func, {
            action: 'Press',
            accessibilityLabel: 'Online test label',
            testID: '12345',
            element: 'Button',
            component: 'HomeScreen',
          })}
        />
      );
    }
  6. Understand instance limitations in @amplitude/unified-react-native

    main

    The unified React Native SDK does not support isolated multiple instances.

    Specifically, the React Native Engagement plugin is a process-wide singleton. If you use createInstance() to create multiple clients, they will all share the same Engagement plugin and native Engagement instance.

    • The first client to initialize supplies the API key and Engagement configuration.
    • Subsequent clients cannot configure an independent Engagement instance.
    • Multiple clients may update the same shared identity through subsequent boot operations.

    Recommendation: Use the package-level singleton exports (init(), track(), etc.) for most applications.

  7. Configure a Custom Transport for Session Replay

    main

    If your environment requires custom request logic (e.g., adding a JWT Authorization header for an authenticated proxy), you can provide handleSendEvents and handleFetchConfig callbacks.

    Key Behaviors

    • URL Resolution: The SDK handles URL resolution (using trackServerUrl and configServerUrl), batching, retries, and compression. Your callback's only job is to execute the request and return a Response.
    • Retries: The SDK calls your callback once per logical attempt. Do not implement your own retry logic; the SDK manages retries and backoff around your callback.
    • Error Handling: Non-2xx responses or rejected promises from your callback are handled by the SDK's existing retry and logging logic.
    • Web Worker Support: If useWebWorker: true is set, the SDK performs compression off the main thread and delegates only the network call back to the main thread to run your callback.
    • Page Exit: For authenticated proxies, the SDK routes the final batch through your callback with keepalive: true instead of using navigator.sendBeacon (which cannot carry custom headers).
    import * as sessionReplay from '@amplitude/session-replay-browser';
    import type { SendEventsRequest, FetchConfigRequest } from '@amplitude/session-replay-browser';
    
    sessionReplay.init(API_KEY, {
      deviceId,
      sessionId,
      trackServerUrl: 'https://my-proxy.example.com/sr-events',
      configServerUrl: 'https://my-proxy.example.com/sr-config',
    
      handleSendEvents: async ({ url, method, headers, body, keepalive }: SendEventsRequest) => {
        return fetch(url, {
          method,
          headers: { ...headers, Authorization: `Bearer ${getJwt()}` },
          body,
          keepalive,
        });
      },
    
      handleFetchConfig: async ({ url, method, headers, signal }: FetchConfigRequest) => {
        return fetch(url, {
          method,
          headers: { ...headers, Authorization: `Bearer ${getJwt()}` },
          signal,
        });
      },
    });
  8. Manage privacy and masking in Session Replay

    main

    By default, Session Replay masks all inputs (displaying ***). You can control privacy using CSS classes or configuration:

    Using CSS Classes

    • Unmask inputs: Add the class .amp-unmask to an <input> to show the actual text.
    • Mask non-input elements: Add the class .amp-mask to any element to mask its text and all children.
    • Block elements: Add the class .amp-block to an element to show it as a blank placeholder with the same dimensions.

    Using CSS Selectors in Config

    You can also block elements globally via the privacyConfig object during plugin instantiation.

    const sessionReplayTracking = sessionReplayPlugin({
      sampleRate: 0.01,
      privacyConfig: {
          blockSelector: ['.ignoreClass', '#ignoreId']
      }
    });