npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-gameanalytics

v0.1.0

Published

A fully-typed React wrapper for the GameAnalytics JavaScript SDK with Next.js support

Readme

react-gameanalytics

A fully-typed React wrapper for the GameAnalytics JavaScript SDK. Provides a React Context + hooks API, a Next.js adapter, and a vanilla JS client for any framework.

GameAnalytics is a leading analytics platform trusted by over 100,000 studios worldwide. It provides real-time player analytics, segmentation, A/B testing, and remote configuration to help developers build, grow, and monetize their applications. The platform is ISO 27001 certified and SOC 2 compliant.

Note: This is a community package, not an official GameAnalytics product.

Features

  • Full SDK coverage - All GameAnalytics event types: Business, Resource, Progression, Design, Error, and Ad events
  • TypeScript-first - Complete type safety with optional generic event typing
  • React 18+ - Context Provider with hooks, SSR-safe
  • Next.js support - App Router and Pages Router adapters with optimized page tracking
  • Framework-agnostic core - Works with Vue, Svelte, vanilla JS, or anything else
  • Bundled SDK - Uses the official gameanalytics NPM package directly (no CDN scripts)
  • Remote Configs - Built-in hooks for GameAnalytics Remote Configs and A/B testing
  • Plugin system - Extensible event pipeline for enrichment, filtering, and custom destinations
  • Consent-ready - Defer initialization until user consent is granted

Installation

npm install react-gameanalytics
# or
yarn add react-gameanalytics
# or
pnpm add react-gameanalytics

Quick Start

React (Next.js, Vite, Remix, etc.)

// app/layout.tsx or src/App.tsx
import { GameAnalyticsProvider } from 'react-gameanalytics/react'

export default function App({ children }) {
  return (
    <GameAnalyticsProvider
      gameKey="your-game-key"
      secretKey="your-secret-key"
    >
      {children}
    </GameAnalyticsProvider>
  )
}
// Any component
import { useGameAnalytics } from 'react-gameanalytics/react'

export function UpgradeButton() {
  const ga = useGameAnalytics()

  return (
    <button
      onClick={() =>
        ga.addBusinessEvent({
          currency: 'USD',
          amount: 2900,
          itemType: 'subscription',
          itemId: 'pro_monthly',
          cartType: 'pricing_page',
        })
      }
    >
      Upgrade to Pro ($29/mo)
    </button>
  )
}

Next.js (with page tracking)

// app/layout.tsx
import { GameAnalyticsProvider } from 'react-gameanalytics/next'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <GameAnalyticsProvider
          gameKey={process.env.NEXT_PUBLIC_GA_GAME_KEY!}
          secretKey={process.env.NEXT_PUBLIC_GA_SECRET_KEY!}
          trackPageViews
        >
          {children}
        </GameAnalyticsProvider>
      </body>
    </html>
  )
}

Vanilla JS (Vue, Svelte, Angular, etc.)

import * as ga from 'react-gameanalytics'

ga.init({
  gameKey: 'your-game-key',
  secretKey: 'your-secret-key',
})

ga.addDesignEvent({ eventId: 'ui:sidebar:toggle' })

Import Paths

| Path | Use case | |---|---| | react-gameanalytics | Vanilla JS client (works everywhere) | | react-gameanalytics/react | React Provider + hooks | | react-gameanalytics/next | Next.js adapter (re-exports React hooks) |


Server Components & SSR

The GameAnalytics SDK is a client-side analytics library that collects data in the browser. Because of this, the GameAnalyticsProvider and all hooks (useGameAnalytics, useRemoteConfig, useTrackPageView) must run in client components.

In Next.js App Router, add the 'use client' directive to any component that calls these hooks:

'use client'

import { useGameAnalytics } from 'react-gameanalytics/react'

export function TrackButton() {
  const ga = useGameAnalytics()
  // ...
}

The provider itself is already marked as a client component, so you only need the directive on your own components that use the hooks.

SSR safety: All SDK calls no-op when running on the server (typeof window === 'undefined'), so the package is safe to use in SSR and SSG environments. No analytics code executes during server rendering or static generation.


Provider Configuration

The GameAnalyticsProvider accepts all configuration options as props:

<GameAnalyticsProvider
  // Required
  gameKey="your-game-key"
  secretKey="your-secret-key"

  // Identity
  userId={user?.id}               // Auto-calls configureUserId on change
  build="1.2.3"                   // App build/release version

  // Logging
  debug={false}                   // Enable info logging (default: false)
  verbose={false}                 // Enable verbose logging (default: false)

  // Auto-tracking
  trackPageViews={false}          // Track route changes as design events

  // Consent
  enabled={true}                  // Set false to defer until consent given

  // Sessions
  manualSessionHandling={false}   // Manage sessions manually

  // Event processing
  eventProcessInterval={8000}     // Event dispatch interval in ms

  // Dimensions (must be set before init)
  customDimensions={{
    dimension01: ['free', 'pro', 'enterprise'],
    dimension02: ['us', 'eu', 'apac'],
    dimension03: ['desktop', 'mobile', 'tablet'],
  }}

  // Resources (must be set before init)
  resourceCurrencies={['credits', 'tokens', 'api_calls']}
  resourceItemTypes={['feature_unlock', 'export', 'integration']}

>
  {children}
</GameAnalyticsProvider>

Event Tracking

useGameAnalytics Hook

import { useGameAnalytics } from 'react-gameanalytics/react'

function MyComponent() {
  const ga = useGameAnalytics()

  // ... use ga methods
}

Business Events

Track purchases and subscription transactions.

// Subscription upgrade
ga.addBusinessEvent({
  currency: 'USD',           // ISO 4217 currency code
  amount: 2900,              // Amount in cents (2900 = $29.00)
  itemType: 'subscription',  // Item category
  itemId: 'pro_monthly',     // Specific plan or product
  cartType: 'pricing_page',  // Optional: where the purchase happened
  customFields: {            // Optional: extra data
    couponCode: 'LAUNCH20',
    trialConverted: true,
  },
})

// One-time purchase
ga.addBusinessEvent({
  currency: 'EUR',
  amount: 4900,
  itemType: 'addon',
  itemId: 'extra_seats_5',
  cartType: 'settings_billing',
})

Resource Events

Track virtual currency or credit usage (earning and spending).

// User earns credits from a referral
ga.addResourceEvent({
  flowType: 'source',        // 'source' (gain) or 'sink' (spend)
  currency: 'credits',       // Virtual currency name
  amount: 500,               // Amount gained/spent
  itemType: 'referral',      // Category
  itemId: 'invite_accepted', // Specific source/sink
})

// User spends API call tokens
ga.addResourceEvent({
  flowType: 'sink',
  currency: 'api_calls',
  amount: 1,
  itemType: 'export',
  itemId: 'csv_export',
})

Progression Events

Track user progress through multi-step flows like onboarding, checkout, or form wizards.

// User starts onboarding
ga.addProgressionEvent({
  status: 'start',              // 'start', 'complete', or 'fail'
  progression01: 'onboarding',  // Required: top-level flow
  progression02: 'profile',     // Optional: current step
})

// User completes onboarding
ga.addProgressionEvent({
  status: 'complete',
  progression01: 'onboarding',
  progression02: 'profile',
  score: 85,                    // Optional: completion score or time in seconds
})

// User abandons checkout
ga.addProgressionEvent({
  status: 'fail',
  progression01: 'checkout',
  progression02: 'payment',
  progression03: 'credit_card',
})

Design Events

Track custom interactions and UI events. Supports a 5-level colon-separated hierarchy.

// UI interaction
ga.addDesignEvent({
  eventId: 'ui:sidebar:toggle',
})

// Feature usage with a value
ga.addDesignEvent({
  eventId: 'feature:dashboard:widget_added',
  value: 4,   // e.g., total widgets after adding
})

// Search behavior
ga.addDesignEvent({
  eventId: 'search:query:results',
  value: 23,  // number of results returned
})

// Deep hierarchy for detailed tracking
ga.addDesignEvent({
  eventId: 'settings:integrations:slack:webhook:created',
})

Error Events

Track application errors.

ga.addErrorEvent({
  severity: 'error',         // 'debug' | 'info' | 'warning' | 'error' | 'critical'
  message: 'Failed to fetch dashboard data: 503 Service Unavailable',
})

// Track a caught exception
try {
  await submitForm(data)
} catch (err) {
  ga.addErrorEvent({
    severity: 'warning',
    message: `Form submission failed: ${err.message}`,
  })
}

Ad Events

Track ad impressions and interactions (for ad-supported apps or content platforms).

ga.addAdEvent({
  adAction: 'show',              // 'clicked' | 'show' | 'failedShow' | 'rewardReceived'
  adType: 'banner',              // 'video' | 'rewardedVideo' | 'playable' | 'interstitial' | 'offerWall' | 'banner'
  adSdkName: 'google_adsense',   // Ad SDK name
  adPlacement: 'article_footer', // Placement identifier
})

// User watches a rewarded video to unlock premium content
ga.addAdEvent({
  adAction: 'rewardReceived',
  adType: 'rewardedVideo',
  adSdkName: 'admob',
  adPlacement: 'unlock_report',
  duration: 30,
})

// Ad failed to show
ga.addAdEvent({
  adAction: 'failedShow',
  adType: 'interstitial',
  adSdkName: 'admob',
  adPlacement: 'between_pages',
  noAdReason: 'noFill',  // 'unknown' | 'offline' | 'noFill' | 'internalError' | 'invalidRequest' | 'unableToPrecache'
})

Custom Fields

Every event method accepts optional customFields for additional data:

ga.addDesignEvent({
  eventId: 'feature:export:csv',
  value: 1,
  customFields: {
    rowCount: 15000,
    fileSize: '2.4MB',
    filters: ['date_range', 'status'],
  },
})

User Identity

Via Provider prop (recommended)

The userId prop is reactive. When it changes (e.g., user logs in), configureUserId is called automatically.

import { useAuth } from './hooks/useAuth'

function App({ children }) {
  const { user } = useAuth()  // Supabase, Firebase, Clerk, etc.

  return (
    <GameAnalyticsProvider
      gameKey="..."
      secretKey="..."
      userId={user?.id}        // undefined when logged out, set when logged in
    >
      {children}
    </GameAnalyticsProvider>
  )
}

Via hook (imperative)

const ga = useGameAnalytics()
ga.setUserId('user_abc123')

Custom Dimensions

Use dimensions to segment your analytics by plan, region, device, or any other attribute.

Set available values at init

<GameAnalyticsProvider
  customDimensions={{
    dimension01: ['free', 'pro', 'enterprise'],
    dimension02: ['us', 'eu', 'apac'],
    dimension03: ['desktop', 'mobile', 'tablet'],
  }}
>

Set active values at runtime

const ga = useGameAnalytics()
ga.setCustomDimension01('pro')        // User's plan
ga.setCustomDimension02('eu')         // User's region
ga.setCustomDimension03('desktop')    // User's device

Global Custom Event Fields

Attach fields to every event automatically:

const ga = useGameAnalytics()

ga.setGlobalCustomEventFields({
  appVersion: '2.4.1',
  environment: 'production',
  organizationId: 'org_xyz',
})

Page View Tracking

Page views are tracked as design events with pathname segments mapped to GA's 5-level hierarchy.

URL: /dashboard/analytics/reports
Design event: pageView:dashboard:analytics:reports

Enable in Next.js

<GameAnalyticsProvider
  gameKey="..."
  secretKey="..."
  trackPageViews
>

With configuration

<GameAnalyticsProvider
  gameKey="..."
  secretKey="..."
  trackPageViews={{
    prefix: 'pageView',                            // First level in hierarchy (default)
    excludePaths: ['/admin/*', '/api/*', '/debug'], // Skip these routes
  }}
>

How paths are mapped

| URL path | Design event ID | |---|---| | / | pageView:root | | /dashboard | pageView:dashboard | | /dashboard/analytics | pageView:dashboard:analytics | | /dashboard/analytics/reports | pageView:dashboard:analytics:reports | | /settings/billing/invoices/2024 | pageView:settings:billing:invoices:2024 | | /a/b/c/d/e/f | pageView:a:b:c:d-e-f (overflow joined) |

The prefix occupies level 1. Pathname segments fill levels 2-5. If the path exceeds 4 segments, the remaining segments are joined with hyphens into the last level (GA supports a max of 5 levels).

Manual page tracking (non-Next.js)

For React Router, Remix, or other routers:

import { useLocation } from 'react-router-dom'
import { useTrackPageView } from 'react-gameanalytics/react'

function PageTracker() {
  const location = useLocation()
  useTrackPageView(location.pathname)
  return null
}

Remote Configs & A/B Testing

Check if ready

const ga = useGameAnalytics()
const isReady = ga.isRemoteConfigsReady()

Get a config value

const ga = useGameAnalytics()
const ctaText = ga.getRemoteConfig('cta_button_text', 'Get Started')

useRemoteConfig hook

Subscribe to remote config updates reactively:

import { useRemoteConfig } from 'react-gameanalytics/react'

function PricingBanner() {
  const bannerText = useRemoteConfig('pricing_banner', 'Try Pro free for 14 days')

  return <p>{bannerText}</p>
}

Get all remote configs

const ga = useGameAnalytics()
const allConfigs = ga.getAllRemoteConfigs()  // Returns JSON string

A/B Testing

const ga = useGameAnalytics()
const testId = ga.getABTestingId()
const variantId = ga.getABTestingVariantId()

Listen for config updates

const ga = useGameAnalytics()

const unsubscribe = ga.onRemoteConfigsReady(() => {
  console.log('Remote configs loaded!')
})

// Later:
unsubscribe()

Session Management

By default, GameAnalytics manages sessions automatically. For manual control:

<GameAnalyticsProvider
  gameKey="..."
  secretKey="..."
  manualSessionHandling
>

Then in your components:

const ga = useGameAnalytics()

// Start when user opens the app
ga.startSession()

// End when user closes or goes idle
ga.endSession()

Consent & Privacy

Defer initialization

Don't load or track anything until the user consents:

function App({ children }) {
  const [hasConsent, setHasConsent] = useState(false)

  return (
    <GameAnalyticsProvider
      gameKey="..."
      secretKey="..."
      enabled={hasConsent}
    >
      {children}
      <CookieBanner onAccept={() => setHasConsent(true)} />
    </GameAnalyticsProvider>
  )
}

When enabled changes from false to true, the SDK initializes and begins tracking. Events fired while enabled is false are silently dropped (not queued).

Toggle at runtime

const ga = useGameAnalytics()
ga.disable()   // Stop all event submission
ga.enable()    // Resume event submission

Typed Events (Advanced)

For stricter type safety, define your event schemas and pass them as a generic:

import { useGameAnalytics } from 'react-gameanalytics/react'

type MyBusinessEvents = {
  upgradeToPro: {
    currency: 'USD' | 'EUR'
    amount: number
    itemType: 'subscription'
    itemId: 'pro_monthly' | 'pro_annual'
  }
  purchaseAddon: {
    currency: 'USD' | 'EUR'
    amount: number
    itemType: 'addon'
    itemId: 'extra_seats_5' | 'extra_seats_10' | 'extra_storage'
  }
}

type MyDesignEvents = {
  'ui:sidebar:toggle': never                    // No extra params
  'feature:dashboard:widget_added': { value: number }
  'search:query:results': { value: number }
}

type MyProgressionEvents = {
  onboardingStart: {
    status: 'start'
    progression01: 'onboarding'
    progression02: string
  }
  onboardingComplete: {
    status: 'complete'
    progression01: 'onboarding'
    progression02: string
    score: number
  }
}

const ga = useGameAnalytics<{
  business: MyBusinessEvents
  design: MyDesignEvents
  progression: MyProgressionEvents
}>()

// Fully typed at compile time:
ga.addBusinessEvent('upgradeToPro', {
  currency: 'USD',
  amount: 2900,
  itemType: 'subscription',
  itemId: 'pro_monthly',
})

ga.addDesignEvent('feature:dashboard:widget_added', { value: 4 })
ga.addDesignEvent('ui:sidebar:toggle')

// Type errors caught at compile time:
ga.addBusinessEvent('upgradeToPro', { currency: 'GBP' })  // Error: 'GBP' not in 'USD' | 'EUR'
ga.addDesignEvent('nonexistent:event')                     // Error: not in MyDesignEvents

Plugin System

Plugins intercept events before they're sent to GameAnalytics. Use them for debugging, enrichment, filtering, or forwarding to other services.

Using built-in plugins

import { DebugPlugin, ConsentPlugin } from 'react-gameanalytics'

const ga = useGameAnalytics()

// Log all events to console
ga.addPlugin(new DebugPlugin({ verbose: true }))

// Gate events behind consent
const consent = new ConsentPlugin()
ga.addPlugin(consent)
consent.grant()    // Allow events
consent.revoke()   // Block events

Writing a custom plugin

import type { GAPlugin } from 'react-gameanalytics'

const enrichWithOrgContext: GAPlugin = {
  name: 'org-context',
  type: 'enrichment',

  execute(event) {
    return {
      ...event,
      params: {
        ...event.params,
        customFields: {
          ...event.params.customFields,
          organizationId: getCurrentOrgId(),
          userRole: getCurrentUserRole(),
        },
      },
    }
  },
}

ga.addPlugin(enrichWithOrgContext)
ga.removePlugin('org-context')

Plugin types

  • enrichment - Modifies events before dispatch. Return null to drop an event.
  • destination - Receives events after dispatch. Use for forwarding to other analytics services.

Vanilla JS API Reference

For non-React usage, the root import provides a complete imperative API:

import * as ga from 'react-gameanalytics'

// Lifecycle
ga.init(config)
ga.enable()
ga.disable()
ga.isInitialized()

// Events
ga.addBusinessEvent(params)
ga.addResourceEvent(params)
ga.addProgressionEvent(params)
ga.addDesignEvent(params)
ga.addErrorEvent(params)
ga.addAdEvent(params)

// Identity
ga.setUserId(id)
ga.setBuild(version)
ga.setCustomDimension01(value)
ga.setCustomDimension02(value)
ga.setCustomDimension03(value)
ga.setGlobalCustomEventFields(fields)

// Sessions
ga.startSession()
ga.endSession()

// Remote Configs
ga.isRemoteConfigsReady()
ga.getRemoteConfig(key, defaultValue?)
ga.getAllRemoteConfigs()
ga.getABTestingId()
ga.getABTestingVariantId()
ga.onRemoteConfigsReady(callback)

// Plugins
ga.addPlugin(plugin)
ga.removePlugin(name)

Framework Examples

Vite + React

// src/main.tsx
import { GameAnalyticsProvider } from 'react-gameanalytics/react'

createRoot(document.getElementById('root')!).render(
  <GameAnalyticsProvider
    gameKey={import.meta.env.VITE_GA_KEY}
    secretKey={import.meta.env.VITE_GA_SECRET}
  >
    <App />
  </GameAnalyticsProvider>
)

Remix

// app/root.tsx
import { GameAnalyticsProvider } from 'react-gameanalytics/react'

export default function App() {
  return (
    <GameAnalyticsProvider gameKey="..." secretKey="...">
      <Outlet />
    </GameAnalyticsProvider>
  )
}

Gatsby

// gatsby-browser.tsx
import { GameAnalyticsProvider } from 'react-gameanalytics/react'

export const wrapRootElement = ({ element }) => (
  <GameAnalyticsProvider gameKey="..." secretKey="...">
    {element}
  </GameAnalyticsProvider>
)

Vue (Composition API)

// composables/useAnalytics.ts
import * as ga from 'react-gameanalytics'
import { onMounted } from 'vue'

export function useAnalyticsInit() {
  onMounted(() => {
    ga.init({ gameKey: '...', secretKey: '...' })
  })
}

// In any component:
import * as ga from 'react-gameanalytics'

ga.addDesignEvent({ eventId: 'ui:menu:open' })

Svelte

<script>
  import * as ga from 'react-gameanalytics'
  import { onMount } from 'svelte'

  onMount(() => {
    ga.init({ gameKey: '...', secretKey: '...' })
  })

  function handleClick() {
    ga.addDesignEvent({ eventId: 'cta:hero:clicked' })
  }
</script>

<button on:click={handleClick}>Get Started</button>

Environment Variables

| Variable | Description | |---|---| | NEXT_PUBLIC_GA_GAME_KEY | GameAnalytics game key (Next.js) | | NEXT_PUBLIC_GA_SECRET_KEY | GameAnalytics secret key (Next.js) | | VITE_GA_KEY | GameAnalytics game key (Vite) | | VITE_GA_SECRET | GameAnalytics secret key (Vite) |

Security note: The game key and secret key are client-side values. They are visible in your bundle. This is expected and how the official GameAnalytics SDK works. Do not confuse these with server-side API keys.


TypeScript Support

The package is written in TypeScript and ships with full type definitions. All event parameters, configuration options, and hook return types are fully typed.

import type {
  GameAnalyticsConfig,
  BusinessEventParams,
  ResourceEventParams,
  ProgressionEventParams,
  DesignEventParams,
  ErrorEventParams,
  AdEventParams,
  ResourceFlowType,
  ProgressionStatus,
  ErrorSeverity,
  AdAction,
  AdType,
  AdError,
  GAPlugin,
  GAEvent,
  EventTypeMap,
} from 'react-gameanalytics'

Requirements

  • React 18+ (for /react and /next subpaths)
  • Next.js 13+ (for /next subpath)
  • Node.js 16+

Contributing

Contributions are welcome! Please open an issue to discuss changes before submitting a PR.

License

MIT