Writing Plugins

Plugins are scripts that enhance the functionality of analytics.js to help solve problems and aid in measuring user interaction. This guide describes the process of writing your own analytics.js plugins. For information about how to use analytics.js plugins in your own implementations, see Using plugins.

Defining a plugin

Plugins are defined via the provide command, which must be invoked with the name of the plugin as the first argument followed by the plugin's constructor function. When the provide command is run, it registers the plugin to be used with the ga() command queue.

The plugin constructor

The following is the most basic example of an analytics.js plugin:

// Defines the plugin constructor.
function MyPlugin() {
  console.log('myplugin has been required...');
}

// Registers the plugin for use.
ga('provide', 'myplugin', MyPlugin);

Plugins need to function correctly even in cases where the global ga object has been renamed, so if you're writing a plugin for third-party use, you should include a check to see if the GoogleAnalyticsObject variable has been set to a string other than 'ga'. The following providePlugin function does this:

// Provides a plugin name and constructor function to analytics.js. This
// function works even if the site has customized the ga global identifier.
function providePlugin(pluginName, pluginConstructor) {
  var ga = window[window['GoogleAnalyticsObject'] || 'ga'];
  if (typeof ga == 'function') {
    ga('provide', pluginName, pluginConstructor);
  }
}

Configuring Plugin Instances

When the ga() command queue executes a require command, it instantiates a new object using the new operator on the provide plugin's constructor function. The constructor is passed the tracker object as its first argument, and any configuration options passed to the require command as its second argument.

Consider the following require command added to the JavaScript tracking snippet:

ga('create', 'UA-XXXXX-Y', 'auto');
ga('require', 'localHitSender', {path: '/log', debug: true});
ga('send', 'pageview');

And the localHitSender code:

function LocalHitSender(tracker, config) {
  this.path = config.path;
  this.debug = config.debug;
  if (this.debug) {
    console.log('localHitSender enabled for path: ' + this.path);
    console.log('on tracker: ' + tracker.get('name'));
  }
}

providePlugin('localHitSender', LocalHitSender);

When the require command is run, the following will be logged to the console (note that the name of the default tracker is "t0"):

// localHitSender enabled for path: /log
// on tracker: t0

Defining Plugin Methods

Plugins can expose their own methods which can be invoked using the ga command queue syntax:

ga('[trackerName.]pluginName:methodName', ...args);

where trackerName is optional and methodName corresponds to the name of a function on the plugin constructors prototype. If methodName does not exist on the plugin or the plugin does not exist, an error will occur.

Example plugin method calls:

// Execute the 'doStuff' method using the 'myplugin' plugin.
ga('create', 'UA-XXXXX-Y', 'auto');
ga('require', 'myplugin');
ga('myplugin:doStuff');

// Execute the 'setEnabled' method of the 'hitCopy' plugin on tracker 't3'.
ga('create', 'UA-XXXXX-Y', 'auto', {name: 't3'});
ga('t3.require', 'hitcopy');
ga('t3.hitcopy:setEnabled', false);

Example plugin method definitions:

// myplugin constructor.
var MyPlugin = function(tracker) {
};

// myplugin:doStuff method definition.
MyPlugin.prototype.doStuff = function() {
  alert('doStuff method called!');
};

// hitcopy plugin.
var HitCopy = function(tracker) {
};

// hitcopy:setEnabled method definition.
HitCopy.prototype.setEnabled = function(isEnabled) {
  this.isEnabled = isEnabled;
}:

Loading plugins

Plugins are typically loaded from a separate JavaScript file or bundled together with your main application code.

<script async src="myplugin.js"></script>

Plugins do not necessarily need to be defined before they are required. Since analytics.js is loaded asynchronously and plugins are often also loaded asynchronously, the ga() command queue is built to handle this.

If the command queue receives a require command for a plugin that has not yet been provided, it will hault execution of the remaining items in the queue until the plugin is available.

The following code shows how the ga('require', 'myplugin') command is not actually executed until the ga('provide', 'myplugin', ...) command is encountered, three seconds later.

ga('require', 'myplugin');

function MyPlugin() {
  console.log('myplugin has been required...');
}

// Waits 3 second after running the `require`
// command before running the `provide` command.
setTimeout(function() {
  ga('provide', 'myplugin', MyPlugin);
}, 3000);

Examples

The following example plugin is designed to capture custom campaign values from a page's URL and pass them to the tracker. This plugin demonstrates how to define and register a plugin script, pass plugin configuration parameters, and define and call plugin methods.

// campaign-loader.js

function providePlugin(pluginName, pluginConstructor) {
  var ga = window[window['GoogleAnalyticsObject'] || 'ga'];
  if (typeof ga == 'function') {
    ga('provide', pluginName, pluginConstructor);
  }
}

/**
 * Constructor for the campaignLoader plugin.
 */
var CampaignLoader = function(tracker, config) {
  this.tracker = tracker;
  this.nameParam = config.nameParam || 'name';
  this.sourceParam = config.sourceParam || 'source';
  this.mediumParam = config.mediumParam || 'medium';
  this.isDebug = config.debug;
};

/**
 * Loads campaign fields from the URL and updates the tracker.
 */
CampaignLoader.prototype.loadCampaignFields = function() {
  this.debugMessage('Loading custom campaign parameters');

  var nameValue = getUrlParam(this.nameParam);
  if (nameValue) {
    this.tracker.set('campaignName', nameValue);
    this.debugMessage('Loaded campaign name: ' + nameValue);
  }

  var sourceValue = getUrlParam(this.sourceParam);
  if (sourceValue) {
    this.tracker.set('campaignSource', sourceValue);
    this.debugMessage('Loaded campaign source: ' + sourceValue);
  }

  var mediumValue = getUrlParam(this.mediumParam);
  if (mediumValue) {
    this.tracker.set('campaignMedium', mediumValue);
    this.debugMessage('Loaded campaign medium: ' + mediumValue);
  }
};

/**
 * Enables / disables debug output.
 */
CampaignLoader.prototype.setDebug = function(enabled) {
  this.isDebug = enabled;
};

/**
 * Displays a debug message in the console, if debugging is enabled.
 */
CampaignLoader.prototype.debugMessage = function(message) {
  if (!this.isDebug) return;
  if (console) console.debug(message);
};

/**
 * Utility function to extract a URL parameter value.
 */
function getUrlParam(param) {
  var match = document.location.search.match('(?:\\?|&)' + param + '=([^&#]*)');
  return (match && match.length == 2) ? decodeURIComponent(match[1]) : '';
}

// Register the plugin.
providePlugin('campaignLoader', CampaignLoader);

The above code can be included into an HTML page as follows:

<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXX-Y', 'auto');
ga('require', 'campaignLoader', {
  debug: true,
  nameParam: 'cname',
  sourceParam: 'csrc',
  mediumParam: 'cmed'
});
ga('campaignLoader:loadCampaignFields');

ga('send', 'pageview');
</script>

<!--Note: plugin scripts must be included after the tracking snippet. -->
<script async src="campaign-loader.js"></script>

Autotrack plugins

The autotrack library is open source, available on Github, and includes several analytics.js plugins that aid in tracking common user interactions. Refer to the autotrack source code to get a better understanding of how plugins work.