Giter VIP home page Giter VIP logo

analytics's Introduction

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 or your own backend.

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 googleAnalytics = require('@analytics/google-analytics')
const customerIo = require('@analytics/customerio')

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]'
})
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: []
  })

  /* 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]'
  })
</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((payload) => {
  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 once.

Arguments

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

Example

// Fire function only once per 'track'
analytics.once('track', ({ payload }) => {
  console.log('This is 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.<string> - 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.<string> - 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:

Plugin Stats Version
@analytics/activity-utils
User activity listener utilities
0.1.16
@analytics/amplitude
Amplitude integration for 'analytics' module
0.1.3
@analytics/aws-pinpoint
AWS Pinpoint integration for 'analytics' module
0.7.12
@analytics/cookie-utils
Tiny cookie utility library
0.2.12
@analytics/core
Lightweight analytics library for tracking events, page views, & identifying users. Works with any third party analytics provider via an extendable plugin system.
0.12.9
@analytics/countly
Countly plugin for 'analytics' module
0.21.12
@analytics/crazy-egg
Crazy Egg integration for 'analytics' module
0.1.2
@analytics/custify
Custify integration for 'analytics' module for browser & node
0.0.2
@analytics/customerio
Customer.io integration for 'analytics' module
0.2.2
@analytics/form-utils
Form utility library for managing HTML form submissions & values
0.3.13
@analytics/fullstory
Unofficial FullStory plugin for 'analytics' module
0.2.6
@analytics/global-storage-utils
Tiny global storage utility library
0.1.7
@analytics/google-analytics
Google analytics v4 plugin for 'analytics' module
1.0.7
@analytics/google-tag-manager
Google tag manager plugin for 'analytics' module
0.5.5
@analytics/google-analytics-v3
Google analytics v3 plugin for 'analytics' module
0.6.1
@analytics/gosquared
GoSquared integration for 'analytics' module
0.1.3
@analytics/hubspot
HubSpot plugin for 'analytics' module
0.5.1
@analytics/intercom
Intercom integration for 'analytics' module for browser & node
1.0.2
@analytics/listener-utils
Backward compatible event listener library for attaching & detaching event handlers
0.4.0
@analytics/localstorage-utils
Tiny LocalStorage utility library
0.1.10
@analytics/mixpanel
Mixpanel plugin for 'analytics' module
0.4.0
@analytics/original-source-plugin
Save original referral source of visitor plugin for 'analytics' pkg
1.0.11
@analytics/ownstats
Ownstats integration for 'analytics' module for browser & node
0.1.2
@analytics/perfumejs
Send browser performance metrics to third-party analytics providers
0.2.1
@analytics/queue-utils
Dependency free queue processor
0.1.2
@analytics/redact-utils
Utility library for redacting event data
0.1.3
@analytics/remote-storage-utils
Storage utilities for cross domain localStorage access, with permissions
0.4.20
@analytics/router-utils
Route change utilities for single page apps
0.1.1
@analytics/scroll-utils
Scroll utility library to fire events on scroll
0.1.22
@analytics/segment
Segment integration for 'analytics' module for browser & node
2.1.0
@analytics/session-storage-utils
Tiny SessionStorage utility library
0.0.7
@analytics/session-utils
Tiny session utility library
0.2.0
@analytics/simple-analytics
Simple analytics plugin for 'analytics' module for browser
0.4.0
@analytics/snowplow
Snowplow integration for 'analytics' module for browser & node
0.3.3
@analytics/storage-utils
Storage utility with fallbacks
0.4.2
@analytics/type-utils
Tiny runtime type checking utils
0.6.2
@analytics/url-utils
Url utils
0.2.3
@analytics/visitor-source
Get visitor source
0.0.7
analytics-cli
CLI for analytics pkg
0.0.5
analytics-plugin-do-not-track
Disable tracking for opted out visitors plugin for 'analytics' module
0.1.5
analytics-plugin-event-validation
Event validation plugin for analytics
0.1.2
gatsby-plugin-analytics
Easily add analytics to your Gatsby site
0.2.0
analytics-plugin-lifecycle-example
Example plugin with lifecycle methods for 'analytics' module
0.1.2
analytics-plugin-tab-events
Expose tab visibility events plugin for 'analytics' module
0.2.1
use-analytics
Analytics hooks for React
1.1.0
analytics-util-params
Url Parameter helper functions
0.1.2
analytics-utils
Analytics utility functions used by 'analytics' module
1.0.12
analytics-plugin-window-events
Expose window events plugin for 'analytics' module
0.0.7

Community Plugins

Below are plugins created outside of this repo:

Additional examples

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.

analytics's People

Contributors

akre54 avatar bingwanged avatar bug-reaper avatar chrisdrackett avatar davidwells avatar deevus avatar dobesv avatar frknbasaran avatar ian avatar itamarm avatar johnparn avatar laurence-hudson-mindfoundry avatar oupsla avatar pnarielwala avatar pulkitsethi avatar rajnandan1 avatar realbisoye avatar rgwozdz avatar robertvo824 avatar scott-fischer avatar stephan281094 avatar thiamsantos avatar thilak-rao avatar tobilg avatar tommedema avatar vojgin avatar wachunei avatar warreh avatar wlecki avatar xanderberkein avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

analytics's Issues

analytics.ready suggested use?

Let's say I want to track a certain view from being rendered:

export default function MyView(props) {
  useEffect(() => {
    analytics.page({ title: 'My View' })
  }, [])

  return (
    <div>
      <p>
        Yeehaw!
      </p>
    </div>
  )
}

Would the above be correct or would you always have to first wait for the analytics library to load? Or is this done internally anyway? E.g.:

  useEffect(() => {
    analytics.ready(() => {
      analytics.page({ title: 'My View' })
    })
  }, [])

Allow for additional PageData properties

The current implementation allows for the PageData attributes to be overridden when calling analytics.page({...}).

It would be beneficial if additional properties could be specified in PageData beyond the standard.

And example of this, is adding additional Context to a Page View event with Snowplow.

For Snowplow, a common use case would be to inject the array of context data. This would look something like:

analytics.page({
  context: [{
    schema: 'iglu:com.acme/blog_post/jsonschema/1-0-0',
    data: {
      title: 'Re-thinking the structure of event data',
      category: 'Data Insights',
      datePublished: '2020-01-24'
    }
  }]
});

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.

analytics.track payload for GA

I'm currently tracking events and have the GA plugin added, but I'm having trouble finding the payload values when browsing events within my GA dashboard. Looking at the GA plugin I'm seeing that it expects label, value, category in the payload (not any key:values we want). Is that correct? I'm somewhat new to the GA interface so I apologize in advance if the data is there and I'm just not finding it. My code looks like this:

analytics.track('chooseStackOption', {
  type: 'framework',
  option: 'next.js'
});

Is there a way to fetch the existing instance of Analytics?

Hi there

I am trying to initialise analytics using the segment plugin, but I am struggling to grab a reference to the created analytics and use it to call page, track and identify calls.

Here is how I got it to work for me so far:

 public page(config: AnalyticsConfig, pageData: PageData) {

    analytics = Analytics({
      app: 'recruiting',
      plugins: [
        segmentPlugin({
          writeKey: config.writeKey
        })
      ]
    });

    analytics.page(pageData);
  }

  public identify(config: AnalyticsConfig, id: string, traits: any) {

    analytics = Analytics({
      app: 'recruiting',
      plugins: [
        segmentPlugin({
          writeKey: config.writeKey
        })
      ]
    });
    analytics.identify(id, traits);
  }

  public track(config: AnalyticsConfig, eventName: string, payload?: any) {


    analytics = Analytics({
      app: 'recruiting',
      plugins: [
        segmentPlugin({
          writeKey: config.writeKey
        })
      ]
    });
    analytics.track(eventName, payload);
  }

I feel that this isn't the best way of doing it as I have to pass through my write key and initialise Analytics every time I make a page, track or identify call.

I am working in Angular 9 + typescript and not sure how to initialise Analytics once and then fetch a reference to the already created Analytics and use it.

Allow for better page tracking in segment

Hi there

I was using angulartics2 as an analytics library before switching to analytics. We are sending our analytics to segement.
I hoping to ask if there is a way to send through page data in a better format similarly to how it was when using angulartics2?

Here is what I mean:
Angulartics2
Screenshot 2020-08-11 at 10 02 46

analytics
Screenshot 2020-08-11 at 10 03 01

I like the fact that analytics sends the height and width as well. I have added the name field to the page track call myself which is working fine but when viewing it in the segment debugger it shows as /recruiting/ instead of Job Postings

Thanks for the hard work :)

Google Tag Manager - Custom Dimensions

Hello,

I'm trying to use custom dimensions variables with @analytics/google-tag-manager plugin.
And this is what I did:

const analytics = Analytics({
  app: 'App-Client',
  plugins: [
    googleTagManager({
      containerId: envHandler.GTM_ACCOUNT_ID
    })
  ]
})

analytics.page({
  'pageTitleCustomDimension': 'Homepage'
})

I did the settings in Google Analytics (I created the custom dimension) and also in Google Tag Manager (I created the dataLayer Variable and linked to the GA custom dimension).

But in GA, the result that I got is the default value of the dataLayer Variable from GTM.

The preview of GTM:
Capture dโ€™eฬcran 2020-05-20 aฬ€ 11 56 59

Every time, I use the page method, it send a Message event with the pushed data layer. It does not fire my Page View Tag.

Is there a documentation on how to use custom dimensions with @analytics/google-tag-manager ?

Thank you.

GA event tracking

First of all: nice work!

How can I migrate my Google Analytics event tracking with a category, action and label to analytics? The event tracking implementation from Google:

ga('send', 'event', [eventCategory], [eventAction], [eventLabel], [eventValue], [fieldsObject]);

Integration (at least Segment) anonymousIds differ from analytics.js anonymousId

For data analysis reasons it is important for the anonymousId to be the same across integrations. We're having difficulties analysing our tracking data because we expected the following two IDs to be the same:

Screen Shot 2019-07-25 at 3 23 41 PM

Do you agree that each integration should retrieve the analytics.js anonymousId if already known when loading? For now we're going to work around this by either by telling segment what analytics.js' anonymousId is or by telling analytics.js what Segment's anonymous ID is, tbd.

And if so, would the same be necessary for the userId?

Google Analytics - Custom Dimensions

Hello everyone ๐Ÿ‘‹

Is there any way to create a Custom dimension with @analytics/google-analytics plugin? I didn't find any information about that in the documentation.

Thank you

Works in development but not in production. [Angular/Electron]

Hey there, love this lib. Spent a whole bunch of time configuring Google Analytics events and it went swimmingly in development. Then I built the application as an Angular/Electron .dmg file and installed... and none of my analytics are showing up at all. No events, pageviews, nothing. Any idea what could cause this issue of not working in prod? Worked beautifully when testing in dev

Internal state isn't update after pageStart dispatch

Hi ๐Ÿ‘‹,

First, I hope everything is ok for you by the current times.

I would love to use your library but, I do have a problem when invoking page() with react-router-dom.

I made a codesandbox to illustrate my problem.

When I navigate between page A, B, C. React-router change the page URI correctly but internal state of analytics seems to be unchanged.


I tried first with history with the listen function then with a useEffect() inside my page components.

The page event is correctly dispatched but the reducer didn't update the URL and stuff because it assumes dispatched data is under action.data.

export default function page(state = initialState, action) {
  switch (action.type) {
    case EVENTS.page:
      return Object.assign({}, state, action.data)
    default:
      return state
  }
}

But dispatch look like that :

store.dispatch({
  type: EVENTS.pageStart,
  properties: getPageData(d),
  options: opts,
  userId: getUserProp(ID, instance, d),
  anonymousId: getUserProp(ANONID, instance, d),
  meta: {
    timestamp: timestamp(),
    callback: resolvePromise(resolve, getCallback(data, options, callback))
  },
})

I think I'm missing something about the current behavior and I would love a bit of help to figure out ๐Ÿ˜Š

Revamp gatsby-plugin-analytics

Hey there,

It seems like gatsby-plugin-analytics hasn't received much attention in a while. I'd love it if we can make use of new features from both Gatsby and Analytics since the last time it was built.

I think gatsby-plugin-analytics should help users initialize the Analytics component as well as window.Anlytics = Analytics and setting up AnalyticsProvider so users can use hook right away.

The way we can go about it is using Gatsby component shadowing feature. Suggested end user experience:

  • yarn add gatsby-plugin-analytics analytics use-analytics @analytics/whatever-plugin
  • in gatsby-config.js:
module.exports = {
  plugins: ["gatsby-plugin-analytics"]
}
  • configure plugins in src/gatsby-plugin-analytics/analytics.js
import Analytics from 'analytics'
import googleAnalytics from '@analytics/google-analytics'

const analytics = Analytics({
  app: 'awesome-app',
  plugins: [
    googleAnalytics({
      trackingId: 'UA-1234567',
    })
  ]
})

export default analytics
  • in component:
import { useAnalytics } from 'use-analytics'

...

const { track } = useAnalytics()
track()

These are just some initial thoughts on what the future Gatsby plugin can do. Would love your ideas and feedback! I'd be happy to help out with this as well! Thanks for building a great suite of analytic-related libraries so far!

Self host analytics.js

})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga')

I'd like to self-host a copy of analytics.js and serve it from my hostname. Would you accept a pull request, if I make it configurable?

The default behavior would remain unchanged and I would like to add an optional configuration to be able to change the URL of analytics.js

PS: Thank you for creating this amazing project.

No support for the Name or Category parameter?

Sample page obj from Segment looks like this..

{
  "messageId": "test-message-ba192f",
  "timestamp": "2020-05-26T22:47:19.726Z",
  "type": "page",
  "email": "[email protected]",
  "projectId": "HUYWRgukrrNoMy2xznmfp",
  "properties": {
    "property1": 1,
    "property2": "test",
    "property3": true
  },
  "userId": "test-user-t1uhd",
  "name": "Home Page"
}

A page obj from this plugin looks roughly like this..

{
    ...,
    "page": {
      ...,
      "path": "path",
      "search": "",
      "title": "title",
      "url": "url"
    },
  "properties": {
    ...,
    "path": "path",
  },
  "type": "page",
  "userId": "userId"
}

As you can see there is no obvious support for 'name' or 'category' values.

Plugin/Integration request: Microsoft AppInsights (Application Insights)

Hello,

Would it be possible to add AppInsights to the list of supported integrations? I think it'd be a "provider plugin"? The JS SDK is on github: https://github.com/Microsoft/ApplicationInsights-JS

The list of tracking methods (e.g. trackEvent, trackPageView, etc):
https://github.com/Microsoft/ApplicationInsights-JS#sending-telemetry-to-the-azure-portal

API reference: https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md

If I try to give you a headstart, assuming I understood your interface, some pseudo-code would be:

import { ApplicationInsights } from '@microsoft/applicationinsights-web'

export default function applicationInsightsPlugin(userConfig) {
    let appInsights;
    return {
        name: 'application-insights-plugin',
        config: {
            /* See https://github.com/Microsoft/ApplicationInsights-JS#configuration */
        },
        initialize: ({ config }) => {
            const appInsights = new ApplicationInsights({
                config: {
                    instrumentationKey: 'INSTRUMENTATION_KEY_GOES_HERE'
                    /* ...Other Configuration Options... */
                }
            });
            appInsights.loadAppInsights();
        },
        page: ({ payload }) => {
            appInsights.trackPageView();
        },
        track: ({ payload }) => {
            appInsights.trackEvent({ name: 'some event' }, data);
        },
        identify: ({ payload }) => {
            appInsights.setAuthenticatedUserContext(authenticatedUserId, accountId, storeInCookie)
        },
        loaded: () => {
            // return boolean so analytics knows when it can send data to third party
            return !!appInsights
        }
    }
}

It is possible to load AppInsights as a script on the page like most trackers, but that's not how we've used it. Hence my pseudo-code uses the npm module.

Integration Request: Facebook Pixel

Currently in the process of integrating analytics, I really like the concept and the library!

Am probably going to end up writing a barebones Facebook Pixel plugin, just thought I would make a ticket to see it. It has a pretty simple API that I think will map well.

Some issues I've encountered:

  • Facebook PageView event doesn't let you tell it what page you're viewing, it takes it straight from the URL. It also does this automatically (even in SPAs) unless you configure disablePushStateTracking
  • It doesn't look like analytics standardises what you pass to identify which makes it hard to make a single identify call if you are for instance using segment and facebook which have different field names.

`identify` call without userId does not result in http request

As described at Segment's documentation, sometimes you don't know a user's ID, but still want to tie their session to traits. In this case you use anonymousId instead of the userId for the identify call.

Using analytics.js this should be as simple as calling analytics.identify(traits) instead of analytics.identify(userId, traits).

However, I'm not seeing any outgoing http requests (using the segment provider) when I do this. When I call analytics.track, I do see an outgoing request.

When I log the first argument of the identify callback it does seem to do what it needs to:

Screen Shot 2019-05-17 at 3 00 57 PM

However, there's no outgoing http request, and segment also doesn't capture the identify call.

Make redux integration optional

Hello,

Are there any plans to remove redux from the core package? It increases the final bundle size, but it is needed only for development mode. Pehaps you can extract it into a separate package?

analytics.reset() w/Segment plugin doesn't clear data

I'm using the Segment plugin

import Analytics from 'analytics';
import segmentPlugin from '@analytics/segment';`

The page and track events are successfully appearing in my Segment console, but it seems analytics.reset() doesn't clear the Segment cookies.
The logout code includes this:

    analytics.track('logout');
    analytics.reset();

When the user logs out, I see the logout event in the Segment dashboard, but all subsequent events keep having the sam userId they had before the reset() call.

Multiple instances of analytics-plugin-google-tag-manager

How to correctly add multiple instances of the same plugin?
I need two instances of the analytics-plugin-google-tag-manager on a page.
As far as I know GTM doesn't forbid it and even has an example https://developers.google.com/tag-manager/devguide#multiple-containers.

This code won't work (error related to namespaces).

  plugins: [
    googleTagManager({containerId: 'GTM-123xxx'}),
    googleTagManager({containerId: 'GTM-123yyy'}),
  ]

I guess I could overcome it with little overriding like this

  plugins: [
    {...googleTagManager({containerId: 'GTM-123xxx'}), NAMESPACE: 'google-tag-manager-1'},
    {...googleTagManager({containerId: 'GTM-123yyy'}), NAMESPACE: 'google-tag-manager-2'},
  ]

But it won't work either - in plugin's initialize method script is being injected only when there is no .../gtm.js script on a page (regardless of it's containerId param).

Maybe scriptLoaded function should be rewritten with containerId parameter?

David, what your suggestions could be on this matter?

Chain plugins to modify payload in sequence

Hi, is it possible to chain multiple plugins to modify the payload so that changes from one plugin are passed to another?

Example:
The setup is like this:

Analytics({
    ...
    plugins: [
        // Plugins that disable tracking on certain conditions
        doNotTrackPlugin(),
        ...
        // plugins that modify the payload
        enrichAnalyticsPlugin(),
        dropNoValuePropertiesPlugin(),
        snakecasePropertiesPlugin(),
        // 3rd party analytics
        googleAnalyticsPlugin({ ... })
    ],
    ...
}

where each of the plugins that modifies the payload changes only a single specific part of it. E.g. this is a plugin which converts the property keys in payload.properties from camelCase to snake_case:

export function snakecasePropertiesPlugin() {
  return {
    name: "snakecase-properties",
    track: ({ payload }) => {
      return {
        ...payload,
        properties: snakeCaseProperties(payload.properties)
      };
    },
    identify: ({ payload }) => {
      return {
        ...payload,
        traits: snakeCaseProperties(payload.traits)
      };
    },
    page: ({ payload }) => {
      return {
        ...payload,
        properties: snakeCaseProperties(payload.properties)
      };
    }
  };

  function snakeCaseProperties(obj) {
    return Object.keys(obj).reduce((agg, key) => {
      agg[_.snakeCase(key)] = obj[key];
      return agg;
    }, {});
  }
}

The assumption is that the plugins that modify the payload should be run for all data that is passed to the Analytics instance, hence I did not want to use the namespacing as described in the docs.

But what this leads to is that instead of the data being passed from plugin to plugin sequentially, they are all fed with the initial data. So when there are multiple plugins modifying the payload before it is sent to the 3rd party analytics, those changes are just overwriting one another when the new returned value is merged with result from previous plugin, instead of those changes compounding.

Is this use case supported?

I assume (haven't tested yet) that in this scenario, it could be solved by namespacing all the modifying plugins? (e.g. so that all their functions are namespaced, e.g. 'track:google-analytics'). But if all these changes should be applied to multiple 3rd party analytics, it would quickly get inconvenient as the same functions would have to be namespaced for all of them? (So each plugin would have to have not only 'track:google-analytics', but also 'track:hubspot', for example).

Additional issue is that with my approach, the order is important (e.g. payload should be first enriched, and only then the value-less properties should be dropped). Could that be achieved with this approach? Or would I have to specify for each plugin the namespace of the plugin that should go after it? (So that in the example above, enrichAnalyticsPlugin needs to be namespaced to dropNoValuePropertiesPlugin, that in turn namespaced to snakecasePropertiesPlugin, and so on, so they are all sequentially run?). The downside of that approach is that I cannot just reorder the plugins easily, but I would have to rename the namespaces for all affected plugins.

Or is there maybe a plugin that could be given a list of other plugins, and subplugins would modify the payload sequentially? Or will I have to squash the plugins into a big one and namespace it to the desired 3rd party analytics plugin?

What's the best course of action here? Thanks.

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: indico/react-overridable#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 ๐Ÿ˜…)

Vue example: middlewares not a function error

Hi, I'm experimenting building out a basic custom plugin which I am looking to add to the vue example. When I add even the most basic custom plugin (like the one in the docs that does nothing) I am getting the following error:

applyMiddleware.js?05f2:39 Uncaught TypeError: middleware is not a function at eval (applyMiddleware.js?05f2:39) at Array.map (<anonymous>) at eval (applyMiddleware.js?05f2:38) at <anonymous>:1:28399 at createStore (createStore.js?61ed:51) at analytics (analytics.browser.es.js?4747:3847) at eval (analytics.js?da7c:4) at Module../src/analytics.js (app.js:2662) at __webpack_require__ (app.js:726) at fn (app.js:101)

I'm guessing its some sort of dependency version problem(?) but would really appreciate any insight.

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 ?

analytics.alias ?

I'm using the Segment plugin, where my Segment account is configured to forward events to Mixpanel. Mixpanel defines its own unique distinct ID for each user and it is necessary for me to call Segment's alias method to then link my user's anonymous ID with Mixpanel's distinct ID. See also 1 and 2 (note that I am not using user IDs as my app does not require registration; instead I use anonymous IDs).

However the analytics API seems to lack the alias function. How would I then call Segment's alias method?

Removing properties from payload.properties or payload.traits in namespaced hooks doesn't propagate to downstream hooks

Current behavior:
If I have 2 plugins with e.g. a track hook, and one plugin defines a namespaced hook to run before the other plugin (track:pluginB in the example below), then when I try to remove a property from payload.property object in the upstream plugin, this change is not propagated to the downstream plugin (see example below).

This is unexpected behavior, as other changes seem to be propagated correctly.

Desired behavior:
If I remove properties in the upstream plugin, the change should be propagated to the downstream plugin and the property should not be defined on the object there.

import { omitBy, isNil } from "lodash";

// plugin A
{
  name: "pluginA",
  "track:pluginB": ({ payload }) {
    // Returns payload with null or undefined values removed from
    // payload.properties
    return {
      ...payload,
      properties: {
        ...omitBy(payload.properties, isNil)
        test: true
    };
  }
  ...
};

// plugin B
{
  name: "pluginB",
  config: { payload },
  track:
    // The printed properties object includes `test: true`, so the payload
    // was clearly passed through pluginA["track:pluginB"], but the object
    // also still includes null and undefined values, although the `omitBy`
    // function removed them.
    console.log(payload.properties);
    return payload;
  ...,
};

Update Snowplow Plugin to support 2.15.0 features

Snowplow JavaScript Tracker 2.15.0 introduces a number of new features, including:

Anonymous Tracking Configuration
Connection Timeout Configuration
Skip Browser Feature Capture
Client Hints Auto Capture

react native support

Currently if you run this in a react native project you get the following error:

TypeError: undefined is not an object (evaluating 'os.indexOf')

I'm guessing that the check to see if this is running in a browser is passing even though react native should probably be treated like a "server" like environment.

TypeError in gtm plugin: undefined is not an object

I have no idea why do I continue to get errors 'TypeError: undefined is not an object (evaluating 'n[t].src.match')'.
Example of report in bugsnag:
https://i.imgur.com/cLfm60Y.png
https://i.imgur.com/H2pgIDn.png
It refers to this lines in source code:

  const scripts = document.querySelectorAll('script[src]')
  return !!Object.keys(scripts).filter(key => scripts[key].src.match(regex)).length

I can't even imagine when scripts[key].src in these lines could be undefined.
Anyway, we can replace scripts[key].src.match with (scripts[key].src || '').match.

Namespaced hooks in plugins use the target plugin's config instead of own config

Current behavior:
If I have 2 plugins, and one plugin defines a namespaced hook to run before the other plugin (track:pluginB in the example below), the config in the namespaced hook refers to the target plugin's config, instead of the config where the function was defined (see example below).

This is unexpected behavior, as one would expect that all hooks defined in single plugin would have access to their plugin's config object.

Desired behavior:
All hooks defined within one plugin should have access to their plugin's config object, regardless of whether they are namespaced or not.

// plugin A
{
    name: "pluginA",
    config: {test: "A"},
    "track:pluginB": ({ config }) {
        // config refers to pluginB's config!
        console.log(config.test) // prints B
    }
    ...
  };

// plugin B
{
    name: "pluginB",
    config: {test: "B"},
    track:
        // config refers to pluginB's config!
        console.log(config.test) // prints B
    ...,
  };

Update Full Story src

Thanks for this amazing package and your hard work!

I'd send a PR, but I'm out of town.

FullStory changed their JS package src to: edge.fullstory.com/s/fs.js

See
https://help.fullstory.com/hc/en-us/articles/360041242094-FullStory-on-a-CDN

I'm getting a lot of "Full Story failed to load" errors with the current src.

If L40 reference below can be changed to edge.fullstory.com/s/fs.js for the next release, that'd be awesome! Thanks again.

script.src = `https://www.fullstory.com/s/fs.js`

Google Analytics with Express?

According to the docs it looks like it is possible to run Google Analytics inside of an express server? I was trying to do that with the following code:

import Analytics from 'analytics';
import googleAnalytics from '@analytics/google-analytics';
import { trackingID } from '../../config/google-analytics';

console.log(trackingID); // verified to be set

export const analytics = Analytics({
  app: 'api',
  plugins: [
    googleAnalytics({
      trackingId: trackingID,
    }),
  ],
});

and then call it in a route with

analytics.track('play', {
    category: 'Videos',
    label: 'Fall Campaign',
    value: 42,
  });

But looking in the realtime google analytics tab I don't see any events. Any idea on what might be wrong?

Missing Project Licence?

The CONTRIBUTING.md file suggests this project is licensed under MIT, but references a LICENSE file that doesn't exist. package.json also doesn't specify the license, nor could I find an explicit license anywhere in the docs.

Am I missing it somewhere, was it just missed, or is the lack of explicit license intentional? Project looks great!

Enable extra config for items such as `cookieFlags`

if (!loadedInstances[instanceName]) {
const gaConfig = {
cookieDomain: config.domain || 'auto',
siteSpeedSampleRate: config.siteSpeedSampleRate || 1,
sampleRate: config.sampleRate || 100,
allowLinker: true,
// useAmpClientId: config.useAmpClientId
}
if (instanceName) {
gaConfig.name = instanceName
}
ga('create', config.trackingId, gaConfig)

I'm working to resolve a current issue where the ga default cookie is not sent with the correct SameSite and Secure flags. In a normal setup you would do something like

ga('create', 'UA-XXXXX-Y', {
  'cookieName': 'gaCookie',
  'cookieDomain': 'blog.example.co.uk',
  'cookieExpires': 60 * 60 * 24 * 28  // Time in seconds.
  'cookieUpdate': 'false',
  'cookieFlags': 'SameSite=None; Secure',
});

With Chrome 84, cookies will now be flagged with an issue in the console if they are not correctly set with the proper attributes. In a future version these cookies will be discarded.

For the short term could we add a extra config object to the plugin method
Something like,

googleAnalytics({trackingId: 'UA-12345678-1'}, {cookieFlags: 'SameSite=None; Secure'})

Then it would be easy to assign that to the gaConfig on line 86.

I'm happy to help where I can.

The google analytics plugin is not loaded with Gatsby in production

I am not sure what might be wrong. It works in development, but in production running window.Analytics.getState() I see this. All tracking requests are in the queue and never transmitted over to GA.

image

You can see the site live at https://lovemyvoice.app/. It's not localized to English yet, but that shouldn't matter. You can see analytics.ts file in devtools (source maps are included).

[email protected]
[email protected]
@analytics/[email protected]
[email protected]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.