Lightweight analytics abstraction layer for tracking page views, custom events, & identifying visitors

Overview

npm npm bundle size GitHub

A lightweight analytics abstraction library for tracking page views, custom events, & identify visitors. Designed to work with any third-party analytics tool.

Read the docs or view the live demo app

Table of Contents

Click to expand

Features

  • Extendable - Bring your own third-party tool & plugins
  • Test & debug analytics integrations with time travel & offline mode
  • Add functionality/modify tracking calls with baked in lifecycle hooks
  • Isomorphic. Works in browser & on server
  • Queues events to send when analytic libraries are loaded
  • Conditionally load third party scripts
  • Works offline
  • TypeScript support

Why

Companies frequently change analytics requirements based on evolving needs. This results in a lot of complexity, maintenance, & extra code when adding/removing analytic services to a site or application.

This library aims to solves that with a simple pluggable abstraction layer.

how analytics works

Driving philosophy:

  • You should never be locked into an analytics tool
  • DX is paramount. Adding & removing analytic tools from your application should be easy
  • Respecting visitor privacy settings & allowing for opt-out mechanisms is crucial
  • A pluggable API makes adding new business requests easy

To add or remove an analytics provider, adjust the plugins you load into analytics during initialization.

Install

This module is distributed via npm, which is bundled with node and should be installed as one of your project's dependencies.

npm install analytics --save

Or as a script tag:

<script src="https://unpkg.com/analytics/dist/analytics.min.js"></script>

Usage

import Analytics from 'analytics'
import googleAnalytics from '@analytics/google-analytics'
import customerIo from '@analytics/customerio'

/* Initialize analytics */
const analytics = Analytics({
  app: 'my-app-name',
  version: 100,
  plugins: [
    googleAnalytics({
      trackingId: 'UA-121991291',
    }),
    customerIo({
      siteId: '123-xyz'
    })
  ]
})

/* Track a page view */
analytics.page()

/* Track a custom event */
analytics.track('userPurchase', {
  price: 20
  item: 'pink socks'
})

/* Identify a visitor */
analytics.identify('user-id-xyz', {
  firstName: 'bill',
  lastName: 'murray',
  email: '[email protected]'
})
Node.js usage

For ES6/7 javascript you can import Analytics from 'analytics' for normal node.js usage you can import like so:

const { Analytics } = require('analytics')
// or const Analytics = require('analytics').default

const analytics = Analytics({
  app: 'my-app-name',
  version: 100,
  plugins: [
    googleAnalyticsPlugin({
      trackingId: 'UA-121991291',
    }),
    customerIOPlugin({
      siteId: '123-xyz'
    })
  ]
})

// Fire a page view
analytics.page()
Browser usage

When importing global analytics into your project from a CDN, the library exposes via a global _analytics variable.

Call _analytics.init to create an analytics instance.

<script src="https://unpkg.com/analytics/dist/analytics.min.js"></script>
<script>
  const Analytics = _analytics.init({
    app: 'my-app-name',
    version: 100,
    ...plugins
  })

  Analytics.track()

  // optionally expose to window
  window.Analytics = Analytics
</script>

Demo

See Analytics Demo for a site example.

API

The core analytics API is exposed once the library is initialized with configuration.

Typical usage:

  1. Initialize with configuration
  2. Export the analytics instance with third-party providers (Google Analytics, HubSpot, etc)
  3. Use page, identify, track in your app
  4. Plugin custom business logic

Configuration

Analytics library configuration

After the library is initialized with config, the core API is exposed & ready for use in the application.

Arguments

  • config object - analytics core config
  • [config.app] (optional) string - Name of site / app
  • [config.version] (optional) string - Version of your app
  • [config.debug] (optional) boolean - Should analytics run in debug mode
  • [config.plugins] (optional) Array.<AnalyticsPlugin> - Array of analytics plugins

Example

import Analytics from 'analytics'
import pluginABC from 'analytics-plugin-abc'
import pluginXYZ from 'analytics-plugin-xyz'

// initialize analytics
const analytics = Analytics({
  app: 'my-awesome-app',
  plugins: [
    pluginABC,
    pluginXYZ
  ]
})

analytics.identify

Identify a user. This will trigger identify calls in any installed plugins and will set user data in localStorage

Arguments

  • userId String - Unique ID of user
  • [traits] (optional) Object - Object of user traits
  • [options] (optional) Object - Options to pass to identify call
  • [callback] (optional) Function - Callback function after identify completes

Example

// Basic user id identify
analytics.identify('xyz-123')

// Identify with additional traits
analytics.identify('xyz-123', {
  name: 'steve',
  company: 'hello-clicky'
})

// Fire callback with 2nd or 3rd argument
analytics.identify('xyz-123', () => {
  console.log('do this after identify')
})

// Disable sending user data to specific analytic tools
analytics.identify('xyz-123', {}, {
  plugins: {
    // disable sending this identify call to segment
    segment: false
  }
})

// Send user data to only to specific analytic tools
analytics.identify('xyz-123', {}, {
  plugins: {
    // disable this specific identify in all plugins except customerio
    all: false,
    customerio: true
  }
})

analytics.track

Track an analytics event. This will trigger track calls in any installed plugins

Arguments

  • eventName String - Event name
  • [payload] (optional) Object - Event payload
  • [options] (optional) Object - Event options
  • [callback] (optional) Function - Callback to fire after tracking completes

Example

// Basic event tracking
analytics.track('buttonClicked')

// Event tracking with payload
analytics.track('itemPurchased', {
  price: 11,
  sku: '1234'
})

// Fire callback with 2nd or 3rd argument
analytics.track('newsletterSubscribed', () => {
  console.log('do this after track')
})

// Disable sending this event to specific analytic tools
analytics.track('cartAbandoned', {
  items: ['xyz', 'abc']
}, {
  plugins: {
    // disable track event for segment
    segment: false
  }
})

// Send event to only to specific analytic tools
analytics.track('customerIoOnlyEventExample', {
  price: 11,
  sku: '1234'
}, {
  plugins: {
    // disable this specific track call all plugins except customerio
    all: false,
    customerio: true
  }
})

analytics.page

Trigger page view. This will trigger page calls in any installed plugins

Arguments

  • [data] (optional) PageData - Page data overrides.
  • [options] (optional) Object - Page tracking options
  • [callback] (optional) Function - Callback to fire after page view call completes

Example

// Basic page tracking
analytics.page()

// Page tracking with page data overrides
analytics.page({
  url: 'https://google.com'
})

// Fire callback with 1st, 2nd or 3rd argument
analytics.page(() => {
  console.log('do this after page')
})

// Disable sending this pageview to specific analytic tools
analytics.page({}, {
  plugins: {
    // disable page tracking event for segment
    segment: false
  }
})

// Send pageview to only to specific analytic tools
analytics.page({}, {
  plugins: {
    // disable this specific page in all plugins except customerio
    all: false,
    customerio: true
  }
})

analytics.user

Get user data

Arguments

  • [key] (optional) string - dot.prop.path of user data. Example: 'traits.company.name'

Example

// Get all user data
const userData = analytics.user()

// Get user id
const userId = analytics.user('userId')

// Get user company name
const companyName = analytics.user('traits.company.name')

analytics.reset

Clear all information about the visitor & reset analytic state.

Arguments

  • [callback] (optional) Function - Handler to run after reset

Example

// Reset current visitor
analytics.reset()

analytics.ready

Fire callback on analytics ready event

Arguments

  • callback Function - function to trigger when all providers have loaded

Example

analytics.ready() => {
  console.log('all plugins have loaded or were skipped', payload)
})

analytics.on

Attach an event handler function for analytics lifecycle events.

Arguments

  • name String - Name of event to listen to
  • callback Function - function to fire on event

Example

// Fire function when 'track' calls happen
analytics.on('track', ({ payload }) => {
  console.log('track call just happened. Do stuff')
})

// Remove listener before it is called
const removeListener = analytics.on('track', ({ payload }) => {
  console.log('This will never get called')
})

// cleanup .on listener
removeListener()

analytics.once

Attach a handler function to an event and only trigger it only once.

Arguments

  • name String - Name of event to listen to
  • callback Function - function to fire on event

Example

// Fire function only once 'track'
analytics.once('track', ({ payload }) => {
  console.log('This will only triggered once when analytics.track() fires')
})

// Remove listener before it is called
const listener = analytics.once('track', ({ payload }) => {
  console.log('This will never get called b/c listener() is called')
})

// cleanup .once listener before it fires
listener()

analytics.getState

Get data about user, activity, or context. Access sub-keys of state with dot.prop syntax.

Arguments

  • [key] (optional) string - dot.prop.path value of state

Example

// Get the current state of analytics
analytics.getState()

// Get a subpath of state
analytics.getState('context.offline')

analytics.storage

Storage utilities for persisting data. These methods will allow you to save data in localStorage, cookies, or to the window.

Example

// Pull storage off analytics instance
const { storage } = analytics

// Get value
storage.getItem('storage_key')

// Set value
storage.setItem('storage_key', 'value')

// Remove value
storage.removeItem('storage_key')

analytics.storage.getItem

Get value from storage

Arguments

  • key String - storage key
  • [options] (optional) Object - storage options

Example

analytics.storage.getItem('storage_key')

analytics.storage.setItem

Set storage value

Arguments

  • key String - storage key
  • value any - storage value
  • [options] (optional) Object - storage options

Example

analytics.storage.setItem('storage_key', 'value')

analytics.storage.removeItem

Remove storage value

Arguments

  • key String - storage key
  • [options] (optional) Object - storage options

Example

analytics.storage.removeItem('storage_key')

analytics.plugins

Async Management methods for plugins.

This is also where custom methods are loaded into the instance.

Example

// Enable a plugin by namespace
analytics.plugins.enable('keenio')

// Disable a plugin by namespace
analytics.plugins.disable('google-analytics')

analytics.plugins.enable

Enable analytics plugin

Arguments

  • plugins String|Array - name of plugins(s) to disable
  • [callback] (optional) Function - callback after enable runs

Example

analytics.plugins.enable('google-analytics').then(() => {
  console.log('do stuff')
})

// Enable multiple plugins at once
analytics.plugins.enable(['google-analytics', 'segment']).then(() => {
  console.log('do stuff')
})

analytics.plugins.disable

Disable analytics plugin

Arguments

  • plugins String|Array - name of integration(s) to disable
  • callback Function - callback after disable runs

Example

analytics.plugins.disable('google').then(() => {
  console.log('do stuff')
})

analytics.plugins.disable(['google', 'segment']).then(() => {
  console.log('do stuff')
})

Events

The analytics library comes with a large variety of event listeners that can be used to fire custom functionality when a specific lifecycle event occurs.

These listeners can be fired using analytics.on & analytics.once

const eventName = 'pageEnd'
analytics.on(eventName, ({ payload }) => {
  console.log('payload', payload)
})

Below is a list of the current available events

Event Description
bootstrap Fires when analytics library starts up.
This is the first event fired. '.on/once' listeners are not allowed on bootstrap
Plugins can attach logic to this event
params Fires when analytics parses URL parameters
campaign Fires if params contain "utm" parameters
initializeStart Fires before 'initialize', allows for plugins to cancel loading of other plugins
initialize Fires when analytics loads plugins
initializeEnd Fires after initialize, allows for plugins to run logic after initialization methods run
ready Fires when all analytic providers are fully loaded. This waits for 'initialize' and 'loaded' to return true
resetStart Fires if analytic.reset() is called.
Use this event to cancel reset based on a specific condition
reset Fires if analytic.reset() is called.
Use this event to run custom cleanup logic (if needed)
resetEnd Fires after analytic.reset() is called.
Use this event to run a callback after user data is reset
pageStart Fires before 'page' events fire.
This allows for dynamic page view cancellation based on current state of user or options passed in.
page Core analytics hook for page views.
If your plugin or integration tracks page views, this is the event to fire on.
pageEnd Fires after all registered 'page' methods fire.
pageAborted Fires if 'page' call is cancelled by a plugin
trackStart Called before the 'track' events fires.
This allows for dynamic page view cancellation based on current state of user or options passed in.
track Core analytics hook for event tracking.
If your plugin or integration tracks custom events, this is the event to fire on.
trackEnd Fires after all registered 'track' events fire from plugins.
trackAborted Fires if 'track' call is cancelled by a plugin
identifyStart Called before the 'identify' events fires.
This allows for dynamic page view cancellation based on current state of user or options passed in.
identify Core analytics hook for user identification.
If your plugin or integration identifies users or user traits, this is the event to fire on.
identifyEnd Fires after all registered 'identify' events fire from plugins.
identifyAborted Fires if 'track' call is cancelled by a plugin
userIdChanged Fires when a user id is updated
registerPlugins Fires when analytics is registering plugins
enablePlugin Fires when 'analytics.plugins.enable()' is called
disablePlugin Fires when 'analytics.plugins.disable()' is called
online Fires when browser network goes online.
This fires only when coming back online from an offline state.
offline Fires when browser network goes offline.
setItemStart Fires when analytics.storage.setItem is initialized.
This event gives plugins the ability to intercept keys & values and alter them before they are persisted.
setItem Fires when analytics.storage.setItem is called.
This event gives plugins the ability to intercept keys & values and alter them before they are persisted.
setItemEnd Fires when setItem storage is complete.
setItemAborted Fires when setItem storage is cancelled by a plugin.
removeItemStart Fires when analytics.storage.removeItem is initialized.
This event gives plugins the ability to intercept removeItem calls and abort / alter them.
removeItem Fires when analytics.storage.removeItem is called.
This event gives plugins the ability to intercept removeItem calls and abort / alter them.
removeItemEnd Fires when removeItem storage is complete.
removeItemAborted Fires when removeItem storage is cancelled by a plugin.

Analytic plugins

The analytics has a robust plugin system. Here is a list of currently available plugins:

Community Plugins

Below are plugins created outside of this repo:

Creating analytics plugins

The library is designed to work with any third-party analytics tool.

Plugins are just plain javascript objects that expose methods for analytics to register and call.

Here is a quick example of a plugin:

// plugin-example.js
export default function pluginExample(userConfig) {
  // return object for analytics to use
  return {
    /* All plugins require a name */
    name: 'my-example-plugin',
    /* Everything else below this is optional depending on your plugin requirements */
    config: {
      whatEver: userConfig.whatEver,
      elseYouNeed: userConfig.elseYouNeed
    },
    initialize: ({ config }) => {
      // load provider script to page
    },
    page: ({ payload }) => {
      // call provider specific page tracking
    },
    track: ({ payload }) => {
      // call provider specific event tracking
    },
    identify: ({ payload }) => {
      // call provider specific user identify method
    },
    loaded: () => {
      // return boolean so analytics knows when it can send data to third-party
      return !!window.myPluginLoaded
    }
  }
}

name is required for all plugins. All other methods are optional.

If you don't need to hook into page tracking, just omit the page key from your plugin object.

To use a plugin, import it and pass it into the plugins array when you bootstrap analytics.

import Analytics from 'analytics'
import pluginExample from './plugin-example.js'

const analytics = Analytics({
  app: 'my-app-name',
  plugins: [
    pluginExample({
      whatEver: 'hello',
      elseYouNeed: 'there'
    }),
    ...otherPlugins
  ]
})

React to any event

Plugins can react to any event flowing through the analytics library.

For example, if you wanted to trigger custom logic when analytics bootstraps, you can attach a function handler to the bootstrap event.

For a full list of core events, checkout events.js.

// Example Plugin plugin.js
export default function myPlugin(userConfig) {
  return {
    /* Name is a required field for plugins */
    name: 'my-plugin',
    /* Bootstrap runs when analytics starts */
    bootstrap: ({ payload, config, instance }) => {
      // Do whatever on `bootstrap` event
    },
    pageStart: ({ payload, config, instance }) => {
      // Fire custom logic before analytics.page() calls
    },
    pageEnd: ({ payload, config, instance }) => {
      // Fire custom logic after analytics.page() calls
    },
    trackStart: ({ payload, config, instance }) => {
      // Fire custom logic before analytics.track() calls
    },
    'track:customerio': ({ payload, config, instance }) => {
      // Fire custom logic before customer.io plugin runs.
      // Here you can customize the data sent to individual analytics providers
    },
    trackEnd: ({ payload, config, instance }) => {
      // Fire custom logic after analytics.track() calls
    },
    // ... hook into other events
  }
}

Using this plugin is the same as any other.

import Analytics from 'analytics'
import customerIoPlugin from '@analytics/customerio'
import myPlugin from './plugin.js'

const analytics = Analytics({
  app: 'my-app-name',
  plugins: [
    // include myPlugin
    myPlugin(),
    customerIoPlugin({
      trackingId: '1234'
    })
    ...otherPlugins
  ]
})

Custom methods

Analytics plugins can provide their own custom functionality via the methods key.

import Analytics from 'analytics'

// Example plugin with custom methods
const pluginOne = {
  name: 'one',
  // ... page, track, etc
  // Custom functions to expose to analytics instance
  methods: {
    myCustomThing(one, two, three) {
      const analyticsInstance = this.instance
      console.log('Use full analytics instance', analyticsInstance)
    },
    otherCustomThing: (one, two, ...args) => {
      // Arrow functions break this.instance context.
      // The instance is instead injected as last arg
      const analyticsInstance = args[args.length - 1]
      console.log('Use full analytics instance', analyticsInstance)
    },
    // Async function examples
    async fireCustomThing(one, two, three) {
      const { track } = this.instance
      track('customThing')
      return 'data'
    },
    triggerSpecial: async (argOne, argTwo, ...args) => {
      // Arrow functions break this.instance context.
      // The instance is instead injected as last arg
      const analyticsInstance = args[args.length - 1]
      return argOne + argTwo
    }
  }
}

// Example plugin with custom methods
const pluginTwo = {
  name: 'two',
  page: () => { console.log('page view fired') }
  // Custom functions to expose to analytics instance
  methods: {
    cookieBanner(one, two, three) {
      const analyticsInstance = this.instance
      console.log('Use full analytics instance', analyticsInstance)
      const cookieSettings = analyticsInstance.storage.getItem('preferences-set')
      if (!cookieSettings) {
        // Show cookie settings
      }
    },
  }
}

// Initialize analytics instance with plugins
const analytics = Analytics({
  app: 'your-app-name',
  plugins: [
    pluginOne,
    pluginTwo
  ]
})

// Using custom methods in your code
analytics.plugins.one.myCustomThing()
analytics.plugins.two.cookieBanner()

Plugin Naming Conventions

Plugins should follow this naming convention before being published to npm

analytics-plugin-{your-plugin-name}

E.g. An analytics plugin that does awesome-stuff should be named

npm install analytics-plugin-awesome-stuff

Then submit to the list above

Debugging analytics

During development, you can turn on debug mode. This will connect the dev tools for you to see the analytics events passing through your application visually.

analytics-debug-tools

import Analytics from 'analytics'

const analytics = Analytics({
  app: 'my-app',
  debug: true
})

TypeScript support

Types for analytics and plugins are generated from JSDoc blocks in the code base via the tsd-jsdoc package.

We are always looking to improve type support & improve the DX of users. If you see something that can be improved let us know in an issue!

Contributing

Contributions are always welcome, no matter how large or small. Before contributing, please read the code of conduct.

Setup & Install dependencies

Clone the repo and run

$ git clone https://github.com/davidwells/analytics
$ cd analytics
$ npm install && npm run setup

The above command will set up all the packages and their dependencies.

Development

You can watch and rebuild packages with the npm run watch command.

npm run watch

While watch mode is activated, you can work against the demo site in examples to test out your changes on a live application.

Comments
  • Adds support for GA4 properties and load `gtag.js` instead of `analytics.js`

    Adds support for GA4 properties and load `gtag.js` instead of `analytics.js`

    I see that under the hood, @analytics/google-analytics loads analytics.js. With Google now making the gtag.js its official JavaScript tagging framework, any plan to update it with gtag?

    Happy to contribute.

    opened by BingWangEd 19
  • Not able to use analytics in angular project

    Not able to use analytics in angular project

    I have a requirement for implementing google analytics for my Angular 8 project in client side. As per your document i tried installing analytics using npm and on importing the analytics library, i am getting an error like below .

    image

    Why i am not able to import it ? @DavidWells

    I am using node v12.13.1.

    Can anyone please help ?

    opened by itsarunyes 18
  • Are there types available?

    Are there types available?

    Hello guys! I am trying to use this analytics script in an existing typescript environment. I tested the vanilla js and it worked pretty well, now I'd like to integrate the tool in my existing typescript application. My question is: are there any types available or is there documentation available for using this with TypeScript? PS: I tried searching in the available documentation in github as well for existing types in npm / TypeSearch, nothing there.

    help wanted 
    opened by katsar0v 18
  • Bump / Upgrade Snowplow to v3 (RENAMED)

    Bump / Upgrade Snowplow to v3 (RENAMED)

    I need to be able to enable the Snowplow Debugger Plugin which seems to not ship with the snowplow tracker (sp.js) but only with their Snowplow plugins.umd.zip

    @DavidWells How would the Analytics wrapper enable me to load additional Snowplow plugins that are not already included in sp.js ?

    The way that the GetAnalytics seems to handle existing plugins is with the instance.plugins.snowplow.enableActivityTracking({ })

    As per Snowplow documentation here they suggest to do the following

    window.snowplow('addPlugin', 
      "https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-debugger@latest/dist/index.umd.min.js",
      ["snowplowDebugger", "DebuggerPlugin"]
    );
    

    When trying to do instance.plugins.snowplow.enableDebugger() then we will get a "AppContext.tsx:139 Uncaught (in promise) TypeError: instance.plugins.snowplow.enableDebugger is not a function"

    However with instance.plugins.snowplow.enableFormTracking() this works because it ships with sp.js

    opened by markojak 15
  • Statements are not allowed in ambient contexts & Cannot find name 'Hook'.

    Statements are not allowed in ambient contexts & Cannot find name 'Hook'.

    Error: node_modules/analytics/lib/types.d.ts:151:4 - error TS1036: Statements are not allowed in ambient contexts. node_modules/analytics/lib/types.d.ts:523:4 - error TS1036: Statements are not allowed in ambient contexts. node_modules/analytics/lib/types.d.ts:555:4 - error TS1036: Statements are not allowed in ambient contexts.

    Error: node_modules/analytics/lib/types.d.ts:562:17 - error TS2304: Cannot find name 'Hook'.

    Node v15.6.0 Any ideas on how to fix this

    opened by lafama 13
  • Create Google Analytics 4 Plugin

    Create Google Analytics 4 Plugin

    Google is officially deprecating Universal Analytics in June 2023 and asking all users to transition to GA4.

    Having a native plugin for implementing GA4 would be a huge help in transitioning our projects without doing a manual implementation.

    opened by claudfuen 11
  • Snowplow v3 update

    Snowplow v3 update

    Closes #202

    I've switched the Snowplow plugin to use @snowplow/browser-tracker so theres no need to load an external assets (e.g. sp.js). However, I had failing builds due to the old commonjs rollup plugin, which was failing on a number of older commonjs dependencies the browser-tracker uses. Upgrading fixes this, but I had to upgrade rollup to v2 as well. I appreciate that has a knock on consequence on every plugin but I've done some testing and everything seems ok from what I can see (and all the assets I tested came out a few kb smaller too 🎉 ).

    If the rollup upgrade isn't desirable, let me know and I can quite switch this to continue using the v3 sp.js rather than bundling the browser-tracker package.

    opened by paulboocock 11
  • Rollup strict mode causing errors when CSP is enabled (eg. WebExtensions)

    Rollup strict mode causing errors when CSP is enabled (eg. WebExtensions)

    When trying to use analytics in a WebExtension there is a strict mode error when bundling.

    In the chrome extensions documentation there is a guide to integrate analytics into the extension, they mention that given the tight Content Security Policies, loading the analytics from google requires to explicitly allow it in the manifest by setting:

    {
      ...,
      "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'",
      ...
    }
    

    Parenthesis: Digging into the code of google analytics plugin I figured out the script is loaded from a different url (https://www.google-analytics.com/analytics.js). My suggestion is to explicitly mention this in the docs.

    But just by importing analytics into my main file I get this error:

    Screen Shot 2020-08-05 at 18 45 27

    I thought it was the url but if looked it up I got this: Screen Shot 2020-08-05 at 18 46 11

    So trying to solve it without having to allow 'unsafe-eval' in the CSP I found this similar issue in other repo: https://github.com/indico/react-overridable/issues/2

    What they did to solve it is to disable strict in their rollup config.

    I have my extension code pushed in a branch if you want to test this. Thank you!

    (ps: for now I'm going to add unsafe eval 😅)

    help wanted 
    opened by wachunei 10
  • Issues with Google Analytics v4

    Issues with Google Analytics v4

    The new version of Google Analytics (v4) seems to not be compatible with the @analytics/google-analytics plugin. Is that something that y'all are aware of or am I just doing something wrong. I noticed that all the docs refer to Universal Analytics (the older version of GA) and seem to use GA-123456 format tracking Ids, where the newer versions are G-123456 format.

    The odd thing is that I see the events firing and it pulling down the single pixel gif with a 200 response, however none of the events seem to be registering in my analytics account. No page views or events are working at all with this lib for me. However if I slap in the script in my HTML with the same tracking Id, it starts working at least for page views.

    I have tried messing around with the cookie domain etc since I am testing on localhost. But again, the domain doesn't seem to be an issue when I use the default GA script so I am not sure what the issue is. Would love some feedback or ideas if y'all have any.

    opened by thom801 9
  • Type error with Snowplow plugin.

    Type error with Snowplow plugin.

    The official demo of page context:

    // or Track page view with additional entities
    analytics.page({
      /**
       * TS2345: Argument of type '{ contexts: { schema: string; data: { title: string; category: string; author: string; datePublished: string; }; }[]; }' is not assignable to parameter of type 'PageData'.   Object literal may only specify known properties, and 'contexts' does not exist in type 'PageData'.
       */
      contexts: [
        {
          schema: 'iglu:com.acme/blog_post/jsonschema/1-0-0',
          data: {
            title: 'Re-thinking the structure of event data',
            category: 'Data Insights',
            author: 'Cara Baestlein',
            datePublished: '2020-01-24'
          }
        }
      ]
    })
    

    Any way to fix it?

    opened by Fnxxxxo 9
  • [Google Analytics] Error: Missing path, href or title in page call for GA

    [Google Analytics] Error: Missing path, href or title in page call for GA

    Hey,

    I am trying to send page view data to google analytics from my node server. However I get the following error:

    (node:63483) UnhandledPromiseRejectionWarning: Error: Missing path, href or title in page call for GA
        at pageView (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/google-analytics/lib/analytics-plugin-ga.cjs.js:106:11)
        at Object.page (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/google-analytics/lib/analytics-plugin-ga.cjs.js:65:7)
        at _callee4$ (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:2441:53)
        at tryCatch (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:1058:40)
        at Generator.invoke [as _invoke] (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:1287:22)
        at Generator.prototype.<computed> [as next] (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:1110:21)
        at asyncGeneratorStep (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:1749:24)
        at _next (/Users/lukasoppermann/Repos/veare/node_modules/@analytics/core/lib/analytics.cjs.js:1771:9)
        at processTicksAndRejections (internal/process/task_queues.js:93:5)
    

    My setup is as follows:

    1. No GA in the frontend (I guess that is the idea)
    2. I have an analytics.ts file that exports the Analytics object and I import it into my routes.ts file where I call the .page method.

    → analytics.ts

    import config from '../config/appConfig'
    import Analytics from 'analytics'
    import googleAnalytics from '@analytics/google-analytics'
    
    export default Analytics({
      app: config.appName,
      plugins: [
        googleAnalytics({
          trackingId: config.trackingId
        })
      ]
    })
    

    → routes.ts

    // ...
        analytics.page(
          // {
          // title: req.path,
          // url: `${config.baseUrl}/${req.path}`,
          // path: req.path
          // }
        )
    // ...
    

    I tried analytics.page without any arguments (as seen above) and with the arguments that are commented out. I also tried only supplying url as shown in the example. However I always get the above mentioned error.

    Possible Fix

    The line with the "bug" is here: https://github.com/DavidWells/analytics/blob/master/packages/analytics-plugin-google-analytics/src/node.js#L58-L59

    I think it needs to be changes to:

      const { path, url, title } = properties
      pageView({ path, url, title }, client)
    

    @DavidWells is that correct? Do you want to quickly fix it or should I send a PR?

    What am I missing here?

    opened by lukasoppermann 9
  • Add configurable page view custom event name for Google Tag Manager

    Add configurable page view custom event name for Google Tag Manager

    The current page implementation is not useful as it does not insert an event key into the event data on the dataLayer. As there is no "standard" event name to use here, this proposes not to send a page event at all unless the user has provided their preferred custom event name. If the event name is provided, this uses it to send the event, which can then be used as a script trigger in the Google Tag Manager interface.

    Fixes https://github.com/DavidWells/analytics/issues/348

    opened by dobesv 0
  • Google Tag Manager page implementation doesn't seem right

    Google Tag Manager page implementation doesn't seem right

    The page implementation in google tag manager just sends the page properties into dataLayer without adding an event key or anything that would make this usable as a GTM trigger.

    I think it could possible add an event key like page_view or maybe just not implement page at all as I don't think there's a standard format for sending page views - GTM just generates its own page view on load and doesn't provide any way to send fake page views that it would treat as a "Page View".

    opened by dobesv 0
  • Consent-aware storage mechanism

    Consent-aware storage mechanism

    Currently analytics by default stores a field __anon_id into localStorage. However, with GDPR we don't want to store any persistent information until the user gives consent to do so.

    We can prevent this by passing in a storage option with a transient storage type (e.g. using @analytics/global-storage-utils).

    However, if the user later gives consent, we would want to transition over to using permanent storage.

    It would be very helpful if the library was "batteries included" in the sense that it provides an easy way to do this, perhaps with a dedicated consent module.

    Initially it could use global storage. But if consent is granted to store tracking cookies, it would copy the relevant data from the global variable storage to localStorage and start using localStorage after that.

    It may also be helpful if the library used a different storage options for different kinds of consent.

    For example, in Google Tag Manager they have ad_storage, analytics_storage, functionality_storage, personalization_storage, and security_storage that are configured and consented to separately.

    I'm not sure if any of the analytics plugins currently would benefit from this distinction but they might.

    If the analytics object had 5 different storage objects and each had a flag indicating whether it had consent or not, and plugins could get an event when storage consent changed, they could make use of that to propagate consent state to their upstream library or other make use of this information to adjust their storage related behavior.

    opened by dobesv 7
  • __anon_id purpose

    __anon_id purpose

    Hi @DavidWells and thank you for this project.

    We are currently integrating this tool in a project. But to acomplish legal requirements (RGPD) we need to explain, and offer opt-in/out, any data stored in the browser. Then,

    whats the purpose of __anon_id stored in localStorage? why is setted once Analytics is initialized? (even with all plugins disabled)

    We have used a plain basic config:

    { app: 'gestio-emocional', debug: false, plugins: { // google-analytics... // google-analytics-v3... }, }

    Our first strike is to initialize Analytics ASAP and enable plugins after user consent, but maybe we have to delay Analytics initialization after user consent.

    Thank you.

    opened by Siot 1
Owner
David Wells
Full stack engineer focused on serverless architecture & product development
David Wells
Ultra lightweight, usable, beautiful autocomplete with zero dependencies.

Awesomplete https://leaverou.github.io/awesomplete/ Awesomplete is an ultra lightweight, customizable, simple autocomplete widget with zero dependenci

Lea Verou 6.9k Jan 2, 2023
An abstraction layer on top of @replit/crosis that makes Repl connection management and operations so easy, a Furret could do it! 🎉

Crosis4Furrets An abstraction layer on top of @replit/crosis that makes Repl connection management and operations so easy, a Furret could do it! ?? In

Ray 18 Dec 29, 2022
Next-gen mobile first analytics server (think Mixpanel, Google Analytics) with built-in encryption supporting HTTP2 and gRPC. Node.js, headless, API-only, horizontally scaleable.

Introduction to Awacs Next-gen behavior analysis server (think Mixpanel, Google Analytics) with built-in encryption supporting HTTP2 and gRPC. Node.js

Socketkit 52 Dec 19, 2022
ZxCDDoS for education with LAYER 7, LAYER 4, AMP METHODS

?? ZxCDDoS: Release v1.0 - Free DDoS Panel ?? Terminal only accepts ANSI color. Username: admin Password: admin Language Logs Fixed L7 methods (crash,

zxcr9999 151 Jan 3, 2023
🦉The jQuery plugin "Stick to me" allows you to create exit-popups on your web page, so you can capture visitors just as they are about to leave.

?? The jQuery plugin "Stick to me" allows you to create exit-popups on your web page, so you can capture visitors just as they are about to leave. How

Guilherme Assemany 18 Feb 10, 2022
Give your JS App some Backbone with Models, Views, Collections, and Events

____ __ __ /\ _`\ /\ \ /\ \ __ \ \ \ \ \ __ ___\ \ \/'\\ \ \_

Jeremy Ashkenas 28k Jan 9, 2023
Give your JS App some Backbone with Models, Views, Collections, and Events

____ __ __ /\ _`\ /\ \ /\ \ __ \ \ \ \ \ __ ___\ \ \/'\\ \ \_

Jeremy Ashkenas 28k Dec 27, 2022
A lightweight abstraction between Svelte stores and Chrome extension storage.

Svelte Chrome Storage A lightweight abstraction between Svelte stores and Chrome extension storage. This library makes data synchronization of backgro

Shaun Wild 10 Nov 15, 2022
An open-source analytics library to measure user events the hassle-free way.

walker.js The walker.js is an open-source event tracker for all tools. Easy, standardized & flexible. Capture user events in the browser by setting HT

elbwalker 181 Dec 22, 2022
A cyber-sec tool to be used responsibly in identifying XSS vulnerabilities

Visit the Breach website here Table of Contents About Breach Getting Started Demo Scan URL Results History Settings Looking Ahead Contributors License

OSLabs Beta 39 Apr 14, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
A Google Earth Engine tool for identifying satellite radar interference

Many military radars interfere with open source satellite imagery when they're turned on. A new tool lets anyone monitor when and where these radars a

Bellingcat 411 Dec 27, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
jQuery plugin that blows your visitors' retinas

Dense.js Homepage | Issues | Dense is a jQuery plugin for serving retina-ready, high pixel ratio images with ease. Small, ease-to-adapt, yet very cust

Jukka Svahn 212 Jun 30, 2022
Easy way to know how many visitors are viewing your Github, Website

Visit Counter Easy way to know how many visitors are viewing your Github, Website Reference for Image query Usage Default Example page It can be your

DevXprite 7 Dec 24, 2022
Single Page Application micro framework. Views, routes and controllers in 60 lines of code

SPApp Single Page Application Micro Framework Supports views, controllers and routing in 60 lines of code! Introduction If you heard anything about MV

Andrew 262 Nov 23, 2022
A simple yet feature-rich counter that allows you to count the views of your Github README files and profile page

View Counter Purpose This is a simple yet feature-rich counter that allows you to count the views of your github README files and profile page. Featur

Toby Hagan 4 Nov 10, 2022